initial commit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 10:06:47 +01:00
commit 7b460dd322
34 changed files with 3413 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
# Ralph Loop Guide
## What is a Ralph Loop?
A Ralph Loop is an autonomous AI development pattern where a language model is invoked repeatedly in a loop, performing exactly one task per iteration. Each iteration is a **fresh context** — the model has no memory of previous runs. Persistence is achieved through files: the model reads project state from disk at the start of each iteration and writes its results back before stopping.
The name originates from [Geoffrey Huntley's technique](https://ghuntley.com/loop/) for autonomous coding with Claude Code. The core insight: by constraining each iteration to a single task and using files as shared memory, you get reliable, incremental progress without context window exhaustion.
Sources and further reading:
- [Geoffrey Huntley — Everything is a Ralph Loop](https://ghuntley.com/loop/)
- [snarktank/ralph — Original implementation](https://github.com/snarktank/ralph)
- [frankbria/ralph-claude-code — Claude Code adaptation](https://github.com/frankbria/ralph-claude-code)
## How Ralph Works in This Project
### Directory Structure
Each Ralph run lives in its own directory under `.ralph/`:
```
.ralph/
└── my-run/
├── instructions.md # Agent prompt (user-created, required)
├── meta.md # Run metadata (auto-generated)
├── chief-wiggum.md # Chief Wiggum directives (auto-generated template)
├── answers.md # Human answers to questions (auto-generated template)
├── questions.md # Questions raised by Ralph (auto-generated template)
├── progress.txt # Iteration log (auto-generated template)
└── run.log # Execution log (auto-generated)
```
### The Iteration Cycle
```
┌─────────────────────────────────┐
│ ralph.sh starts iteration N │
│ │
│ 1. Read project state │
│ (files listed in │
│ instructions.md) │
│ │
│ 2. Select ONE task │
│ (by priority order) │
│ │
│ 3. Execute that task │
│ (edit files as needed) │
│ │
│ 4. Append summary to │
│ progress.txt │
│ │
│ 5. Stop │
│ (or emit COMPLETE signal) │
└────────────┬────────────────────┘
ralph.sh checks output
for <promise>COMPLETE</promise>
┌────┴────┐
│ Found? │
├─Yes─────┼──► Loop ends
└─No──────┘
Sleep 2s, start iteration N+1
```
### File Roles
Each run directory contains files with specific roles:
| File | Purpose | Who writes it |
|------|---------|---------------|
| `instructions.md` | Defines what Ralph does, which files to read, task priorities | Human (before run) |
| `progress.txt` | Append-only iteration log — Ralph's memory across iterations | Ralph |
| `chief-wiggum.md` | Directives from the Chief Wiggum session | Chief Wiggum |
| `answers.md` | Human answers to Ralph's questions | Human |
| `questions.md` | Questions Ralph raises when encountering ambiguity | Ralph |
| `meta.md` | Run metadata (date, model, config) | Auto-generated |
| `run.log` | Execution log (start/stop times, iteration count) | ralph.sh |
## How to Run Ralph
### Basic Usage
```bash
./ralph.sh .ralph/my-run
```
This runs the default configuration: up to 20 iterations, using Claude Opus, with Read/Edit/Write tools.
### Prerequisites
The run directory must contain an `instructions.md` file. All other files are auto-created as templates if they don't exist.
### Options
```bash
./ralph.sh <run-directory> [options]
Options:
-n, --max-iterations N Maximum iterations (default: 20)
-m, --model MODEL Claude model to use (default: opus)
-t, --tools TOOLS Allowed tools, quoted (default: "Read Edit Write")
```
### Examples
```bash
# Short run with cheaper model
./ralph.sh .ralph/my-run -n 10 -m sonnet
# Run with more tools (e.g. for coding tasks)
./ralph.sh .ralph/my-run -n 30 -t "Read Edit Write Bash Glob Grep"
# New run
mkdir .ralph/my-new-task
# Write .ralph/my-new-task/instructions.md, then:
./ralph.sh .ralph/my-new-task -n 15
```
### Path Convention in instructions.md
Use `{{RUN_DIR}}` as a placeholder for run-specific files. The script substitutes it automatically before passing the prompt to Claude:
```
{{RUN_DIR}}/progress.txt # run-specific
{{RUN_DIR}}/chief-wiggum.md # run-specific
{{RUN_DIR}}/answers.md # run-specific
{{RUN_DIR}}/questions.md # run-specific
```
All other file paths are relative to the project root as usual.
## How to Communicate with Ralph
Ralph cannot be talked to directly. Communication happens through files:
### As Chief Wiggum (directing Ralph)
Edit `chief-wiggum.md` in the run directory **before** starting the loop:
- **`## Action Required`** — Ralph MUST address these, one per iteration, in order.
- **`## Observations`** — Informational notes. Ralph may use them but is not required to act.
Ralph never modifies `chief-wiggum.md`. To confirm an item was addressed, check `progress.txt`.
### As the Human (answering questions)
When Ralph raises a question in `questions.md`, write your answer in `answers.md` in the run directory. On the next iteration, Ralph will process the answer.
### Monitoring Progress
- **`progress.txt`** — Append-only log of every iteration. What Ralph did and why.
- **`run.log`** — Execution metadata (start time, iterations, completion status).
- **Project files** — Diff the files Ralph was allowed to edit to see actual changes.
## Writing instructions.md
The `instructions.md` file defines Ralph's behavior for a run. Key elements:
1. **Role definition** — What is Ralph doing? (Refining stories, writing code, reviewing, ...)
2. **One-task-per-iteration rule** — Always enforce this. Without it, the model tries to do everything at once and produces lower-quality results.
3. **Startup sequence** — Which files to read and in what order. This is Ralph's "memory load." Use `{{RUN_DIR}}` for run-specific files.
4. **Task selection priorities** — A strict priority list. Ralph picks the first applicable task, does it, and stops.
5. **File ownership table** — What Ralph may read vs. write. Prevents accidental overwrites.
6. **Completion signal** — How Ralph signals that all work is done (`<promise>COMPLETE</promise>`).
7. **Guardrails** — Rules to prevent Ralph from making unauthorized decisions.
## Best Practices
### Do
- **Keep iterations small.** One task per iteration produces better results than batching.
- **Use progress.txt as memory.** Ralph reads it every iteration. Be explicit about what was done and what was deferred.
- **Clean up chief-wiggum.md** between runs. Remove addressed items to avoid confusion.
- **Review diffs after the loop.** Ralph is autonomous but not infallible. Spot-check the changes.
- **Use the right model.** Opus for complex reasoning tasks, Sonnet for straightforward work.
- **Match tools to the task.** Read/Edit/Write for spec work. Add Bash/Glob/Grep for coding tasks.
### Don't
- **Don't modify files Ralph owns during a run.** File conflicts will confuse the model.
- **Don't give Ralph tools it doesn't need.** Minimal tool sets reduce unintended side effects.
- **Don't expect Ralph to remember across iterations.** Every iteration is a fresh context. If it's not in a file, it doesn't exist.
- **Don't skip instructions.md.** Running without a structured prompt loses the task selection that makes Ralph reliable.

View File

@@ -0,0 +1,124 @@
---
name: ralph-prep
description: Prepare a new Ralph Loop run — create the run directory under .ralph/ and interactively generate instructions.md. Use this whenever the user wants to set up a Ralph loop, prepare a new autonomous task, or says "/ralph-prep". Also trigger when the user mentions creating Ralph instructions or setting up an agent loop.
---
# Ralph Loop Preparation
You are helping the user set up a new Ralph Loop run. A Ralph Loop is an autonomous agent pattern where Claude is invoked repeatedly, performing one task per iteration with no memory between iterations. Persistence happens through files.
Read `.claude/rules/ralph-loops.md` for the full guide if you need context on how Ralph works.
## Your Job
1. Interview the user to understand the task
2. Create the run directory under `.ralph/`
3. Generate a complete `instructions.md`
The conversation language is German (per project statutes). The generated files are in English.
## Interview
Ask these questions to understand the task. You don't need to ask them one-by-one in a rigid sequence — have a natural conversation in German. But make sure you cover all of them before generating the file.
### Required
1. **Run name** — short, kebab-case identifier (e.g. `implement-auth`, `refine-api-spec`). This becomes the directory name under `.ralph/`.
2. **Role** — What is Ralph doing? One sentence. (e.g. "Refine user stories until implementation-ready", "Implement the event creation API endpoint with TDD", "Review all components for accessibility issues")
3. **Startup files** — Which files should Ralph read at the start of each iteration to understand the project state? The following are always included automatically — don't ask the user about these:
- `{{RUN_DIR}}/progress.txt`
- `{{RUN_DIR}}/chief-wiggum.md`
- `{{RUN_DIR}}/answers.md`
- `{{RUN_DIR}}/questions.md`
- `CLAUDE.md`
Ask the user what else Ralph needs — spec files, source code, config files, etc.
4. **Writable files** — Which files may Ralph modify? Be specific. Ralph should never modify files it only needs for context.
5. **Task priorities** — What should Ralph do in each iteration? Help the user define a priority list. The first two priorities (Chief Wiggum action items, process answers) and the last one (completion signal) are always the same. Help the user define the middle priorities that are specific to their task.
### Optional (suggest if relevant)
6. **Guardrails** — Any decisions Ralph must NOT make on its own? (e.g. "do not choose dependencies", "do not modify the database schema without asking")
## Generating instructions.md
Use the following structure. Adapt the content based on the interview answers — don't include sections that aren't relevant to the task.
```markdown
# Ralph Loop — {role title}
{One paragraph describing what Ralph does in this run.}
## CRITICAL RULE: One Task Per Iteration
You MUST perform exactly ONE task per iteration. Not two, not "a few small ones", not "all remaining items". ONE.
After completing your single task:
1. Append a short summary of what you did to `{{RUN_DIR}}/progress.txt`.
2. Stop. Do not look for more work. Do not "while I'm at it" anything.
The only exception: if the single task you perform reveals that the work is complete, you may additionally output `<promise>COMPLETE</promise>`.
## Startup: Read Project State
At the start of every iteration, read these files in order:
1. `{{RUN_DIR}}/progress.txt` — what previous iterations did (your memory across iterations).
2. `{{RUN_DIR}}/chief-wiggum.md` — notes from Chief Wiggum. Items under `## Action Required` have highest priority.
3. `{{RUN_DIR}}/answers.md` — check if the human answered any open questions.
4. `{{RUN_DIR}}/questions.md` — open and resolved questions.
5. `CLAUDE.md` — project statutes and principles.
{additional startup files, numbered starting at 6}
## Task Selection (Priority Order)
Pick the FIRST applicable task from this list. Do that ONE task, then stop.
### Priority 1: Chief Wiggum action items
If `{{RUN_DIR}}/chief-wiggum.md` has items under `## Action Required`, address the FIRST one that hasn't been addressed yet (check `{{RUN_DIR}}/progress.txt`). Do NOT modify `{{RUN_DIR}}/chief-wiggum.md`.
### Priority 2: Process answers
If `{{RUN_DIR}}/answers.md` contains an answer, process it. Remove the processed entry from `{{RUN_DIR}}/answers.md`.
{task-specific priorities — numbered starting at 3}
### Priority N: Complete
If all work is done, all answers processed, and all Chief Wiggum action items addressed:
Output `<promise>COMPLETE</promise>` and stop.
## File Ownership
Respect these boundaries strictly:
| File | Owner | You may... |
|------|-------|------------|
| `{{RUN_DIR}}/progress.txt` | Ralph | Read and append |
| `{{RUN_DIR}}/questions.md` | Ralph | Read and write |
| `{{RUN_DIR}}/answers.md` | Human | **Read only.** Only remove entries you have already processed. |
| `{{RUN_DIR}}/chief-wiggum.md` | Chief Wiggum | **Read only.** Never modify. |
| `CLAUDE.md` | Human | **Read only.** Never modify. |
{additional file ownership rows from interview}
{optional: ## Handling Uncertainty section with question format}
{optional: ## Rules section with task-specific constraints}
```
### Key conventions
- Always use `{{RUN_DIR}}` for paths to files inside the run directory. The script substitutes this at runtime.
- All other paths are relative to the project root.
- The one-task-per-iteration rule and the `<promise>COMPLETE</promise>` signal are non-negotiable — always include them.
- The first two priorities (Chief Wiggum items, process answers) and the last (complete) are always present. Task-specific priorities go in between.
## After Generating
1. Create the directory: `mkdir -p .ralph/{run-name}`
2. Write `instructions.md` into it
3. Give the user a complete, copy-ready command to start the loop. Pick sensible defaults for `-n` and `-m` based on the task complexity (e.g. simple verification tasks: `-n 10 -m sonnet`, complex coding tasks: `-n 30 -m opus`). Example:
```
./ralph.sh .ralph/verify-impl-order -n 10 -m sonnet
```
4. Briefly mention the other options (`-n`, `-m`, `-t`) in case they want to adjust

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
# IDE
.idea/
.vscode/
*.iml
# OS
.DS_Store
Thumbs.db
# Claude Code (machine-local)
.claude/settings.local.json

View File

@@ -0,0 +1,5 @@
# Answers
<!-- This file is YOUR file. Write your answers to open questions from questions.md here. -->
<!-- Ralph will pick them up in the next iteration. -->

View File

@@ -0,0 +1,111 @@
# Ralph Loop — User Story Refinement
You are an autonomous agent running inside a Ralph Loop. Your job is to refine existing user stories and setup tasks until they are implementation-ready.
## CRITICAL RULE: One Task Per Iteration
You MUST perform exactly ONE task per iteration. Not two, not "a few small ones", not "all remaining items". ONE.
After completing your single task:
1. Append a short summary of what you did to `{{RUN_DIR}}/progress.txt`.
2. Stop. Do not look for more work. Do not "while I'm at it" anything.
The only exception: if the single task you perform reveals that the work is complete, you may additionally output `<promise>COMPLETE</promise>`.
## Startup: Read Project State
At the start of every iteration, read these files in order:
1. `{{RUN_DIR}}/progress.txt` — what previous iterations did (your memory across iterations).
2. `{{RUN_DIR}}/overseer.md` — notes from the overseer. Items under `## Action Required` have highest priority.
3. `{{RUN_DIR}}/answers.md` — check if the human answered any open questions.
4. `CLAUDE.md` — project statutes and principles.
5. `Ideen.md` — project vision, features, and binding design decisions.
6. `spec/userstories.md` — the user stories you are refining.
7. `spec/setup-tasks.md` — technical setup tasks.
8. `spec/personas.md` — persona definitions.
9. `{{RUN_DIR}}/questions.md` — open and resolved questions.
## Task Selection (Priority Order)
Pick the FIRST applicable task from this list. Do that ONE task, then stop.
### Priority 1: Overseer action items
If `{{RUN_DIR}}/overseer.md` has items under `## Action Required`, address the FIRST one. Note in `{{RUN_DIR}}/progress.txt` which item you addressed and how. Do NOT modify `{{RUN_DIR}}/overseer.md`.
### Priority 2: Process answers
If `{{RUN_DIR}}/answers.md` contains an answer: move the corresponding question from `## Open` to `## Resolved` in `{{RUN_DIR}}/questions.md` (include the answer), update the affected story or task accordingly, and remove the processed answer from `{{RUN_DIR}}/answers.md`. Process ONE answer, then stop.
### Priority 3: Raise a question
If you discover an ambiguity, inconsistency, or assumption that requires a human decision — and no existing question in `{{RUN_DIR}}/questions.md` already covers it — add ONE question to `{{RUN_DIR}}/questions.md` under `## Open`. Then stop. Do NOT attempt to resolve it yourself.
### Priority 4: Refine one story or task
Pick the next story or task that could be improved. Refinement means:
- **Dependencies:** Add a `**Dependencies:**` field after Notes listing story/task IDs that must be implemented first (e.g. `US-1, T-1`). Use `None` if the story is independent. Do NOT simply extract cross-references already mentioned in the Notes or Acceptance Criteria — those are hints, not the full picture. You must independently reason about each story: what data, API endpoints, UI components, or infrastructure does this story require at runtime that another story or task provides? Think about it from scratch for every story, even if the existing text mentions no other stories at all. Dependencies are stories whose output this story structurally requires — not merely stories that mention related concepts.
- **Consistency:** Does the story use the correct token terminology? Are cross-references to other stories accurate? Are localStorage key names consistent across stories?
- **Completeness:** Are there missing edge cases? Missing acceptance criteria? Unclear behavior?
- **Scope:** Is the story too large for a single implementation effort? If so, propose a split.
- **Testability:** Is every acceptance criterion concrete and verifiable?
- **Alignment:** Does the story align with `Ideen.md` decisions, `CLAUDE.md` statutes, and `spec/personas.md` capabilities/limitations?
- **Overlap:** Does the story duplicate behavior already covered by another story?
- **Setup tasks:** Are the setup tasks complete and aligned with the decided tech stack and architecture?
Work through stories in order (US-1, US-2, ... US-17, then T-1, T-2, T-3). Track which stories you have already reviewed in `{{RUN_DIR}}/progress.txt` so you don't re-review them. If a story needs no changes, note it as "reviewed, no changes needed" in `{{RUN_DIR}}/progress.txt` and stop — that counts as your one task for this iteration.
### Priority 5: Cross-cutting review
After all individual stories and tasks have been reviewed, do ONE cross-cutting check:
- Are all features from `Ideen.md` covered?
- Are persona capabilities/limitations consistent with what the stories allow?
- Is the status counter at the top of `spec/userstories.md` accurate?
- Are there any cross-story inconsistencies that individual reviews missed?
### Priority 6: Complete
If all stories and tasks are refined, all cross-cutting checks pass, all answers are processed, and all overseer action items are addressed:
Output `<promise>COMPLETE</promise>` and stop.
## File Ownership
Respect these boundaries strictly:
| File | Owner | You may... |
|------|-------|------------|
| `spec/userstories.md` | Ralph | Read and write |
| `spec/setup-tasks.md` | Ralph | Read and write |
| `spec/personas.md` | Ralph | Read and write |
| `{{RUN_DIR}}/progress.txt` | Ralph | Read and append |
| `{{RUN_DIR}}/questions.md` | Ralph | Read and write |
| `{{RUN_DIR}}/answers.md` | Human | **Read only.** Only remove entries you have already processed. |
| `{{RUN_DIR}}/overseer.md` | Overseer | **Read only.** Never modify. |
| `Ideen.md` | Human | **Read only.** Never modify. |
| `CLAUDE.md` | Human | **Read only.** Never modify. |
## Handling Uncertainty
You MUST NOT make architectural or design decisions on your own. Check the "Architekturentscheidungen die NOCH NICHT getroffen wurden" section in `Ideen.md`.
- **If a question affects the core of a story** (answer could require a rewrite): Do NOT change the story. Add a question to `{{RUN_DIR}}/questions.md` instead.
- **If a question only affects a detail** (only individual criteria would change): Mark it with `⚠️ ASSUMPTION:` in the Notes section and add a question to `{{RUN_DIR}}/questions.md`.
### Question Format
```
### Q-{number}: {short title}
**Context:** {why this question came up}
**Affects:** {which stories/tasks are affected}
**Options:** {possible answers, if applicable}
```
## Rules
- ONE task per iteration. No exceptions.
- Derive all content strictly from `Ideen.md`. Do not invent features.
- Respect binding decisions in `Ideen.md` ("Getroffene Designentscheidungen").
- Respect project statutes in `CLAUDE.md`.
- Keep stories small and focused. One story = one user-facing capability.
- Acceptance criteria must be concrete and testable.
- Write everything in English (per statutes: code and documentation in English).
- Use the correct persona roles: "event organizer" vs "guest" vs "self-hoster" vs "user" (when both roles apply).
- After modifying any story, update the status counters at the top of `spec/userstories.md`.
- Always append a summary of your single task to `{{RUN_DIR}}/progress.txt` before stopping.

View File

@@ -0,0 +1,7 @@
# Run: review-findings
- **Created**: 2026-03-03
- **Description**: Addressing review findings — overseer action items from the story-refinement review (setup-task dependencies, forward-references, story splits, implementation phases).
- **Model**: opus
- **Max iterations**: 20
- **Status**: Completed

View File

@@ -0,0 +1,25 @@
# Overseer Notes
<!-- This file is written by the human's primary Claude session (the "overseer"). -->
<!-- Ralph reads this file each iteration and must address any items marked as ACTION REQUIRED. -->
<!-- Items marked as OBSERVATION are informational — Ralph may use them but is not required to act. -->
## Action Required
- **Add setup-task dependencies to all user stories that require project infrastructure.** Currently no user story references T-1 or T-2 as a dependency. This is misleading for a greenfield project where no code exists yet. Every story that requires backend infrastructure (Spring Boot project, DB connection) must depend on T-1 and T-2. Every story that requires only frontend infrastructure must depend on at least T-1. Stories that are purely client-side with no build requirement (e.g. US-7 reading localStorage, US-17 CSS toggle) may remain at `Dependencies: None` but should get a note explaining they require the frontend scaffold from T-1 to be practically implementable. Review each story individually and add the appropriate setup-task dependencies.
- **Create T-4: Development infrastructure setup.** T-1 scaffolds empty projects, T-2 creates the Dockerfile and DB connection config. But there is no task for the development foundation needed before the first user story can be implemented with TDD (as required by CLAUDE.md). T-4 should cover: (1) DB migration framework setup (Flyway or Liquibase) with a first empty migration, (2) SPA router configuration for Svelte, (3) API client layer in the frontend (fetch wrapper or similar), (4) Test infrastructure for both backend (JUnit + integration test setup, e.g. Testcontainers) and frontend (Vitest). T-4 depends on T-1 and T-2. All user stories that previously depended on T-1/T-2 should now depend on T-4 instead (since T-4 transitively includes T-1 and T-2).
- **Mark forward-references in acceptance criteria as deferred.** Several stories contain ACs that reference features from stories that will be implemented later. These must be explicitly marked so implementors know they are not part of the initial story scope. The specific forward-references to mark: US-2 AC 5 (cancelled state → deferred until US-18), US-2 AC 6 (deleted after expiry → deferred until US-12, and "or US-19" deferred until US-19), US-3 AC 10 (cancelled event block → deferred until US-18), US-8 AC 9 (STATUS:CANCELLED → deferred until US-18), US-12 AC 2 (stored header images → deferred until US-16). Use the format `[deferred until US-X is implemented]` inline in the AC text. Do NOT remove the ACs — they remain as documentation of the complete behavior, just clearly marked as not part of the initial implementation scope.
- **Split US-10 (Update Messages) into two stories.** US-10 contains two separable concerns with 11 ACs total: (a) posting, displaying, and deleting update messages (organizer posts, public display, deletion — the server-side feature), and (b) the "new update" badge/indicator based on localStorage tracking (the client-side read-state feature). Split into US-10a (Update Messages: post, display, delete) and US-10b (Update Messages: new-update indicator). US-10b depends on US-10a. Update the status counter, personas, and any cross-references in other stories accordingly.
- **Add practical-dependency notes to US-7 and US-14.** Both stories say `Dependencies: None` which is technically correct (US-7 renders from localStorage, US-14 is PWA infrastructure). But for a greenfield project this is misleading. US-7 is only meaningfully testable after US-1, US-3, or US-6 populate localStorage with data. US-14's service worker needs actual assets and pages to cache. Add a note to each story explaining when it becomes practically useful, without changing the Dependencies field. Example: "Note: This story has no structural dependencies but is only meaningfully testable after [US-X, US-Y] provide data/content."
- **Create an implementation phase plan document.** Based on the dependency graph across all stories and setup tasks, create a new file `implementation-phases.md` that documents a recommended implementation order grouped into phases. The phases should respect all declared dependencies, group parallelizable work, and reflect the practical reality that some stories (e.g. US-18, US-19) are only meaningfully testable after their transitive dependencies exist. Include a note that Phase 0 (setup tasks) must complete before any user story work begins.
## Observations
- US-1 is implicitly the "durchstich" (vertical slice) story that bootstraps the entire stack: DB table, backend endpoint, frontend form, localStorage, routing. This is expected for the first story in a greenfield project. No split necessary, but implementors should be aware it will take significantly longer than subsequent stories. If it turns out to be too large during implementation, it can be split into backend-only and frontend-only at that point.
- Q-5 (CI/CD platform) remains open and blocks T-3. This should be resolved before the first story is complete, so code has a pipeline from the start.
- US-15 (event color themes) and US-17 (dark/light mode) have a noted interaction: event themes must remain readable in both dark and light mode. Both stories already mention this in their notes, but no AC defines concrete behavior. This is a design decision that should be made before implementing either story.

View File

@@ -0,0 +1,151 @@
# Ralph Loop Progress Log
# Each iteration appends its findings and decisions here.
# Previous refinement phase (29 iterations) archived to progress-refinement-phase-1.txt.
# Starting phase 2: addressing review findings from review-findings.md.
## Iteration 1 (Phase 2)
**Task:** Overseer action item #1 — Add setup-task dependencies to all user stories that require project infrastructure.
**Changes made to userstories.md:**
- US-1: Dependencies: None → T-1, T-2 (backend API + DB)
- US-2: US-1 → US-1, T-1, T-2 (backend API)
- US-3: US-2 → US-2, T-1, T-2 (backend API + DB)
- US-4: US-1 → US-1, T-1, T-2 (backend API)
- US-5: US-1 → US-1, T-1, T-2 (backend API)
- US-6: US-2 → US-2, T-1 (frontend only, localStorage-based)
- US-7: None → None, added note that T-1 frontend scaffold is needed to be practically implementable (purely client-side)
- US-8: US-2 → US-2, T-1, T-2 (server-side .ics generation)
- US-9: US-2, US-5 → US-2, US-5, T-1, T-2 (server-side field tracking)
- US-10: US-1, US-2 → US-1, US-2, T-1, T-2 (backend API)
- US-11: US-2 → US-2, T-1, T-2 (server-side QR generation)
- US-12: US-1 → US-1, T-1, T-2 (server-side scheduled job)
- US-13: US-1 → US-1, T-1, T-2 (server-side config)
- US-14: None → T-1 (frontend infrastructure: manifest + service worker)
- US-15: US-1, US-2 → US-1, US-2, T-1, T-2 (server-side theme persistence)
- US-16: US-1, US-2 → US-1, US-2, T-1, T-2 (server-side proxy + image storage)
- US-17: None → None, added note that T-1 frontend scaffold is needed to be practically implementable (purely client-side CSS toggle)
- US-18: US-1 → US-1, T-1, T-2 (server-side cancellation state)
- US-19: US-1 → US-1, T-1, T-2 (server-side deletion)
## Iteration 2 (Phase 2)
**Task:** Overseer action item #2 — Create T-4: Development infrastructure setup.
**Changes made:**
- Created T-4 in `setup-tasks.md` with 6 ACs covering: DB migration framework (Flyway/Liquibase), SPA router for Svelte, API client layer, backend test infrastructure (JUnit 5 + Testcontainers), frontend test infrastructure (Vitest), and runnable test commands. T-4 depends on T-1 and T-2.
- Updated all user story dependencies from T-1/T-2 to T-4 (since T-4 transitively includes both):
- US-1: T-1, T-2 → T-4
- US-2: US-1, T-1, T-2 → US-1, T-4
- US-3: US-2, T-1, T-2 → US-2, T-4
- US-4: US-1, T-1, T-2 → US-1, T-4
- US-5: US-1, T-1, T-2 → US-1, T-4
- US-6: US-2, T-1 → US-2, T-4
- US-8: US-2, T-1, T-2 → US-2, T-4
- US-9: US-2, US-5, T-1, T-2 → US-2, US-5, T-4
- US-10: US-1, US-2, T-1, T-2 → US-1, US-2, T-4
- US-11: US-2, T-1, T-2 → US-2, T-4
- US-12: US-1, T-1, T-2 → US-1, T-4
- US-13: US-1, T-1, T-2 → US-1, T-4
- US-14: T-1 → T-4
- US-15: US-1, US-2, T-1, T-2 → US-1, US-2, T-4
- US-16: US-1, US-2, T-1, T-2 → US-1, US-2, T-4
- US-18: US-1, T-1, T-2 → US-1, T-4
- US-19: US-1, T-1, T-2 → US-1, T-4
- US-7 and US-17 remain at Dependencies: None; updated their notes to reference T-4 instead of T-1.
- T-3 left unchanged (setup task, not a user story; its T-1/T-2 dependencies are direct, not via T-4).
## Iteration 3 (Phase 2)
**Task:** Overseer action item #3 — Mark forward-references in acceptance criteria as deferred.
**Changes made to userstories.md:**
- US-2 AC 5: Added `[deferred until US-18 is implemented]` to the cancelled-state criterion
- US-2 AC 6: Added `[deferred until US-12 is implemented]` and `[deferred until US-19 is implemented]` to the event-not-found criterion (also added the US-19 deletion scenario which was missing from the parenthetical examples)
- US-3 AC 11: Added `[deferred until US-18 is implemented]` to the cancelled-event RSVP block criterion
- US-8 AC 9: Added `[deferred until US-18 is implemented]` to the STATUS:CANCELLED calendar criterion
- US-12 AC 2: Added `[deferred until US-16 is implemented]` to the stored header images reference in the cleanup criterion
All ACs remain in place as documentation of complete behavior — only marked so implementors know they are not part of the initial story scope.
## Iteration 4 (Phase 2)
**Task:** Overseer action item #4 — Split US-10 (Update Messages) into two stories.
**Changes made:**
- Split US-10 into US-10a and US-10b in `userstories.md`:
- US-10a (Post update messages as organizer): server-side feature — posting, displaying, and deleting update messages. 6 ACs. Dependencies: US-1, US-2, T-4.
- US-10b (New-update indicator for guests): client-side read-state tracking — badge/indicator for unread updates via localStorage. 5 ACs. Dependencies: US-10a.
- Updated cross-references in US-12 AC 2 and US-19 AC 3: "(US-10)" → "(US-10a)"
- Updated `personas.md`:
- Event Organizer: US-10 → US-10a in capabilities and Appears in
- Guest: Split "See organizer update messages (US-10)" into US-10a (as reader) and US-10b; updated Appears in
- Updated status counter: 19 → 20 total stories (and remaining)
## Iteration 5 (Phase 2)
**Task:** Overseer action item #5 — Add practical-dependency notes to US-7 and US-14.
**Changes made to userstories.md:**
- US-7: Extended the existing practical-implementability note to also explain that the story is only meaningfully testable after US-1, US-3, or US-6 populate localStorage with event data (without those, only the empty state can be tested).
- US-14: Added a note clarifying that while the story depends only on T-4 structurally, the service worker and manifest are only meaningfully testable after other stories (e.g. US-2, US-7) provide actual pages and assets to cache.
## Iteration 6 (Phase 2)
**Task:** Overseer action item #6 — Create implementation phase plan document.
**Changes made:**
- Created `implementation-phases.md` with 6 phases (Phase 05) grouping all setup tasks and user stories by dependency order:
- Phase 0: Project Infrastructure (T-1 → T-2 → T-4, T-3 parallel)
- Phase 1: Core Event Flow vertical slice (US-1 → US-2 → US-3, strictly sequential)
- Phase 2: Organizer Management & Event Lifecycle (US-4, US-5, US-12, US-13, US-18, US-19 — parallelizable, all depend on US-1)
- Phase 3: Enhanced Event Page Features (US-6, US-8, US-9, US-10a, US-10b, US-11 — mostly parallelizable, depend on US-2)
- Phase 4: Visual Customization (US-15, US-16 — depend on US-1 + US-2)
- Phase 5: App Shell & PWA (US-7, US-14, US-17 — minimal structural deps, practically need earlier phases)
- Included: recommended order within each phase, parallelizability notes, deferred AC tracking table, and ASCII dependency graph
- Noted that US-17 can be pulled earlier (Phase 2/3 timeframe) to facilitate US-15's dark/light mode interaction
- Noted T-3 is blocked on Q-5 resolution
- All 6 overseer action items are now addressed.
## Iteration 7 (Phase 2)
**Task:** Process answer to Q-5 (CI/CD platform choice).
**Answer received:** (a) Gitea — project optimized for Gitea, only Gitea infrastructure used.
**Changes made:**
- Moved Q-5 from Open to Resolved in `questions.md` with the answer included.
- Updated T-3 in `setup-tasks.md`: removed ⚠️ ASSUMPTION warning, specified Gitea Actions (`.gitea/workflows/`), Gitea container registry, and updated ACs and notes to reflect the concrete platform choice.
- Removed processed answer from `answers.md`.
## Iteration 8 (Phase 2)
**Task:** Review US-10a (Post update messages as organizer) — created in Phase 2 iteration 4 but never individually reviewed.
**Changes made to userstories.md:**
- Added two missing acceptance criteria for organizer token authentication and server-side enforcement:
- "If the organizer token is absent or invalid, the compose and delete UI is not shown and the server rejects any attempt to post or delete update messages"
- "No account or additional authentication step is required beyond the organizer token"
- This aligns US-10a with the auth pattern established by all other organizer stories (US-4 ACs 2/6/7, US-5 ACs 3/7/8, US-18 ACs 9/10/12, US-19 ACs 6/7/9). Without these ACs, an implementor might not enforce server-side auth on the update message endpoints.
- All other aspects verified: Dependencies correct (US-1 for organizer token + event, US-2 for public display, T-4 for infrastructure). Token terminology consistent. All ACs (now 8) concrete and testable. Cancelled-event behavior documented in Notes (posting not blocked, only expiry blocks). No overlap with US-10b (client-side read tracking), US-9 (field change highlighting). Aligned with Ideen.md and personas. Scope appropriate (single cohesive capability).
- Remaining unreviewed Phase 2 creations: US-10b, T-4.
## Iteration 9 (Phase 2)
**Task:** Review US-10b (New-update indicator for guests) — created in Phase 2 iteration 4 but never individually reviewed.
**Result:** Reviewed, no changes needed.
- Dependencies correct: US-10a is the sole dependency. US-10a transitively provides US-1, US-2, and T-4. Unlike US-7/US-17 (which have zero story deps and need practical-dependency notes), US-10b has a direct story dependency covering all transitive needs.
- Consistency verified: Token terminology correct ("event token" for localStorage keying). `updates_last_seen_at` key explicitly distinct from US-9's `last_seen_at`. Cross-references to US-10a and US-9 accurate.
- Completeness verified: Edge cases (organizer deletes newest update, expired event, no updates, first visit) all handled correctly by existing ACs or produce correct behavior without additional criteria.
- Scope appropriate: Small, focused, single cohesive capability (client-side read-state tracking).
- All 5 ACs concrete and testable.
- Aligned with Ideen.md ("pro Device wird via LocalStorage gemerkt"), CLAUDE.md statutes, and personas.md.
- No overlap with US-9 (field change highlighting vs. free-form announcement awareness).
- Remaining unreviewed Phase 2 creations: T-4.
## Iteration 10 (Phase 2)
**Task:** Review T-4 (Development infrastructure setup) — created in Phase 2 iteration 2 but never individually reviewed.
**Result:** Reviewed, no changes needed.
- Dependencies correct: T-1 (project scaffolds) and T-2 (DB connection config/Dockerfile) are both needed. T-1 provides the empty projects to build upon; T-2 establishes the PostgreSQL datasource configuration that the migration framework wires into.
- Consistency verified: Cross-references accurate — notes correctly state all user stories depend on T-4 transitively. Tech stack references (Flyway/Liquibase, JUnit 5, Testcontainers, Vitest) match Ideen.md decisions.
- Completeness verified: All 6 ACs match the overseer's original action item #2 specification exactly (migration framework, SPA router, API client, backend test infra, frontend test infra, runnable commands). No missing concerns for the "empty scaffold → TDD-ready" gap.
- Scope appropriate: Single cohesive setup task — all items are development foundation work that logically belongs together.
- All 6 ACs concrete and testable (specific tools, commands, and observable outcomes).
- Aligned with CLAUDE.md's TDD mandate and Ideen.md's decided tech stack.
- No overlap with T-1 (scaffolds), T-2 (deployment), or T-3 (CI/CD).
- All Phase 2 creations (US-10a, US-10b, T-4) are now reviewed.
## Iteration 11 (Phase 2)
**Task:** Cross-cutting review — verify consistency after Phase 2 changes.
**Checks performed:**
1. Status counter: 20 total, 0 complete, 20 remaining — matches actual story count. ✓
2. Persona consistency: US-10a/US-10b split correctly reflected in Event Organizer and Guest personas (capabilities and Appears in). ✓
3. Deferred AC references: All 6 deferred markers use consistent format `[deferred until US-X is implemented]`, AC numbers match current story text, referenced stories are correct. ✓
4. Dependency graph in implementation-phases.md: All 20 stories + 4 tasks verified against ASCII graph — all edges and transitive relationships accurate. ✓
5. localStorage key consistency: All keys (event data, organizer token, RSVP data, bookmarks, `last_seen_at`, `updates_last_seen_at`, dark/light preference) are distinct and consistently referenced across stories. ✓
6. Token terminology: "event token" and "organizer token" used consistently across all 20 stories. ✓
**Finding:** implementation-phases.md had stale Q-5 references — Phase 0 intro said "T-3 may run in parallel with Phase 1 once Q-5 is resolved" and table said "Blocked on Q-5." Q-5 was resolved in iteration 7 (Gitea). Updated both references to reflect resolved state.
**No other cross-story inconsistencies found.** All Phase 2 changes (dependency additions, T-4 creation, US-10 split, deferred AC markers, practical-dependency notes) are internally consistent.
- Cross-cutting checks completed for Phase 2: consistency verified ✓. Remaining cross-cutting checks from Phase 1 (Ideen.md coverage, persona consistency, status counter) were re-verified as part of this check — all still valid after Phase 2 changes.

View File

@@ -0,0 +1,66 @@
# Open Questions
<!-- Questions that need human decision before certain user stories can be written or finalized. -->
<!-- Format: Each question gets a unique ID, status (open/resolved), and affected stories if known. -->
## Open
(No open questions.)
## Resolved
### Q-5: CI/CD platform choice
**Context:** T-3 originally assumed Gitea as the CI/CD platform (Gitea Actions with `.gitea/workflows/`, publishing to Gitea's container registry). However, this choice is not documented in Ideen.md or CLAUDE.md. The CI/CD platform determines the workflow file format, container registry configuration, and available pipeline features. The platform-independent requirements (run tests, build, publish on success) are clear, but the implementation depends on where the repository is hosted.
**Affects:** T-3
**Options:**
- (a) Gitea Actions (`.gitea/workflows/`, Gitea container registry) — if the project is hosted on a Gitea instance
- (b) GitHub Actions (`.github/workflows/`, GitHub Container Registry or Docker Hub) — if the project is hosted on GitHub
- (c) GitLab CI (`.gitlab-ci.yml`, GitLab Container Registry) — if the project is hosted on GitLab
- (d) Other platform or defer CI/CD to the self-hoster's responsibility
**Answer:** (a) Gitea. The project shall be optimized for Gitea. Only Gitea infrastructure will be used — Gitea Actions for CI/CD pipelines, Gitea container registry for Docker image publishing.
### Q-1: Event page customization scope ("Designbar")
**Context:** Ideen.md mentions under the event landing page description: "Irgendwie auch Designbar, sofern man das will" (somehow also designable, if you want to). This clearly implies some form of visual customization for event pages, but the scope is entirely undefined. The answer fundamentally changes what the user story would look like — from a trivial theme picker to a complex layout system.
**Affects:** US-15 (written based on this answer).
**Options:**
- (a) The organizer can choose from a small set of predefined themes or color schemes when creating/editing an event
- (b) The organizer can set a custom accent color and/or upload a header image for the event page
- (c) More elaborate customization (custom CSS, layout options, etc.)
- (d) "Designbar" was aspirational / nice-to-have and is deferred or out of scope for the initial version
**Answer:** (a) predefined themes, plus as a separate feature the organizer can search/select a header image from Unsplash (server-proxied to maintain privacy). US-15 written accordingly.
### Q-2: PWA start URL target
**Context:** US-14 (PWA installability) requires a `start_url` in the web app manifest — the page shown when a user launches the installed app from their home screen. The natural candidates are the local event overview (US-7) or a generic landing/home page.
**Affects:** US-7 (route changed to `/`), US-14 (start URL updated).
**Options:**
- (a) Start URL points to the local event overview (`/my-events`) — returning users immediately see their tracked events
- (b) Start URL points to a home/landing page that offers both event creation and a link to the local overview
**Answer:** The local event overview lives at the root page `/`. That is also the start URL. The page should show a project header/branding and the list of events the user attends or watches underneath. US-7 and US-14 updated accordingly.
### Q-3: Progressive enhancement requirement (no-JavaScript content rendering)
**Context:** Several user stories originally included acceptance criteria requiring content to be renderable without JavaScript (e.g. US-2: "core content must be in server-rendered HTML", US-10: "reading updates does not require JavaScript"). This presupposes a progressive enhancement / server-side rendering strategy — but the rendering strategy is explicitly listed as undecided in Ideen.md. The app is also specified as a PWA, which typically implies heavy JavaScript usage. These two forces are in tension: progressive enhancement favors server-rendered HTML enhanced by JS, while a PWA/SPA approach favors client-side rendering. The answer determines whether stories need "works without JS" acceptance criteria or not.
**Affects:** US-2, US-10, US-11, and all page-rendering stories. Assumption notes removed from affected stories.
**Options:**
- (a) Progressive enhancement is a hard requirement: all core content (event details, attendee list, update messages) must be in server-rendered HTML; JavaScript enhances the experience but is not required to view content
- (b) JavaScript-dependent rendering is acceptable: the app can be a SPA with client-side rendering, as long as it meets accessibility standards (screen reader compatibility, semantic HTML in the DOM, etc.)
- (c) Hybrid: critical public pages (event landing page) must work without JavaScript for maximum reach; internal pages (local overview, organizer views) may require JavaScript
**Answer:** SPA with PWA, no server-side rendering. Classic frontend + RESTful API. JavaScript-dependent rendering is acceptable. Accessibility must be ensured through proper semantic HTML and ARIA attributes in the client-rendered DOM. Assumption notes cleaned up in US-2, US-10, and US-11.
### Q-4: Organizer authentication mechanism (token format and transmission)
**Context:** Multiple user stories (US-1, US-4, US-5) assume that organizer authentication works via a secret token stored in localStorage on the device where the event was created. This is a reasonable default in the no-account design, but Ideen.md explicitly lists "Wie genau die Organizer-Authentifizierung technisch funktioniert (Token-Format, Übertragungsweg)" as an undecided architectural decision. Alternative approaches — such as a special organizer URL with the secret embedded — would have different UX and security trade-offs. A URL-based approach would enable cross-device organizer access (the organizer can share or bookmark the management link) while a localStorage-based approach is device-bound but keeps the secret out of URLs, browser history, and referrer headers.
**Affects:** US-1, US-4, US-5, and indirectly US-10. Assumption notes removed from affected stories.
**Options:**
- (a) localStorage-based: the server returns a secret organizer token after event creation; the client stores it in localStorage; organizer access is device-bound
- (b) URL-based: the server returns a special organizer URL containing the secret (e.g. `/events/{id}/manage/{secret}`); the organizer bookmarks or shares this URL; no localStorage needed for auth
- (c) Hybrid: the organizer receives a secret URL after creation, and the app also stores the token in localStorage for convenience; either mechanism grants access
**Answer:** Confirmed localStorage-based approach. The user suggested the event's unique ID could double as the organizer access token but left the door open for separation. Recommendation applied: the organizer secret token is kept separate from the public event ID, because the event link is designed to be shared in group chats — using a single token for both guest access and organizer management would allow any link recipient to edit or delete event data. Existing story acceptance criteria (which already assumed separate tokens) are confirmed correct. Assumption notes removed from US-1, US-4, US-5.

View File

@@ -0,0 +1 @@
=== Run completed (archived) | model=opus | max=20 ===

View File

@@ -0,0 +1,4 @@
# Answers
<!-- Human answers to open questions. Ralph processes these one per iteration. -->
<!-- This run is archived — no pending answers. -->

View File

@@ -0,0 +1,111 @@
# Ralph Loop — User Story Refinement
You are an autonomous agent running inside a Ralph Loop. Your job is to refine existing user stories and setup tasks until they are implementation-ready.
## CRITICAL RULE: One Task Per Iteration
You MUST perform exactly ONE task per iteration. Not two, not "a few small ones", not "all remaining items". ONE.
After completing your single task:
1. Append a short summary of what you did to `{{RUN_DIR}}/progress.txt`.
2. Stop. Do not look for more work. Do not "while I'm at it" anything.
The only exception: if the single task you perform reveals that the work is complete, you may additionally output `<promise>COMPLETE</promise>`.
## Startup: Read Project State
At the start of every iteration, read these files in order:
1. `{{RUN_DIR}}/progress.txt` — what previous iterations did (your memory across iterations).
2. `{{RUN_DIR}}/overseer.md` — notes from the overseer. Items under `## Action Required` have highest priority.
3. `{{RUN_DIR}}/answers.md` — check if the human answered any open questions.
4. `CLAUDE.md` — project statutes and principles.
5. `Ideen.md` — project vision, features, and binding design decisions.
6. `spec/userstories.md` — the user stories you are refining.
7. `spec/setup-tasks.md` — technical setup tasks.
8. `spec/personas.md` — persona definitions.
9. `{{RUN_DIR}}/questions.md` — open and resolved questions.
## Task Selection (Priority Order)
Pick the FIRST applicable task from this list. Do that ONE task, then stop.
### Priority 1: Overseer action items
If `{{RUN_DIR}}/overseer.md` has items under `## Action Required`, address the FIRST one. Note in `{{RUN_DIR}}/progress.txt` which item you addressed and how. Do NOT modify `{{RUN_DIR}}/overseer.md`.
### Priority 2: Process answers
If `{{RUN_DIR}}/answers.md` contains an answer: move the corresponding question from `## Open` to `## Resolved` in `{{RUN_DIR}}/questions.md` (include the answer), update the affected story or task accordingly, and remove the processed answer from `{{RUN_DIR}}/answers.md`. Process ONE answer, then stop.
### Priority 3: Raise a question
If you discover an ambiguity, inconsistency, or assumption that requires a human decision — and no existing question in `{{RUN_DIR}}/questions.md` already covers it — add ONE question to `{{RUN_DIR}}/questions.md` under `## Open`. Then stop. Do NOT attempt to resolve it yourself.
### Priority 4: Refine one story or task
Pick the next story or task that could be improved. Refinement means:
- **Dependencies:** Add a `**Dependencies:**` field after Notes listing story/task IDs that must be implemented first (e.g. `US-1, T-1`). Use `None` if the story is independent. Do NOT simply extract cross-references already mentioned in the Notes or Acceptance Criteria — those are hints, not the full picture. You must independently reason about each story: what data, API endpoints, UI components, or infrastructure does this story require at runtime that another story or task provides? Think about it from scratch for every story, even if the existing text mentions no other stories at all. Dependencies are stories whose output this story structurally requires — not merely stories that mention related concepts.
- **Consistency:** Does the story use the correct token terminology? Are cross-references to other stories accurate? Are localStorage key names consistent across stories?
- **Completeness:** Are there missing edge cases? Missing acceptance criteria? Unclear behavior?
- **Scope:** Is the story too large for a single implementation effort? If so, propose a split.
- **Testability:** Is every acceptance criterion concrete and verifiable?
- **Alignment:** Does the story align with `Ideen.md` decisions, `CLAUDE.md` statutes, and `spec/personas.md` capabilities/limitations?
- **Overlap:** Does the story duplicate behavior already covered by another story?
- **Setup tasks:** Are the setup tasks complete and aligned with the decided tech stack and architecture?
Work through stories in order (US-1, US-2, ... US-17, then T-1, T-2, T-3). Track which stories you have already reviewed in `{{RUN_DIR}}/progress.txt` so you don't re-review them. If a story needs no changes, note it as "reviewed, no changes needed" in `{{RUN_DIR}}/progress.txt` and stop — that counts as your one task for this iteration.
### Priority 5: Cross-cutting review
After all individual stories and tasks have been reviewed, do ONE cross-cutting check:
- Are all features from `Ideen.md` covered?
- Are persona capabilities/limitations consistent with what the stories allow?
- Is the status counter at the top of `spec/userstories.md` accurate?
- Are there any cross-story inconsistencies that individual reviews missed?
### Priority 6: Complete
If all stories and tasks are refined, all cross-cutting checks pass, all answers are processed, and all overseer action items are addressed:
Output `<promise>COMPLETE</promise>` and stop.
## File Ownership
Respect these boundaries strictly:
| File | Owner | You may... |
|------|-------|------------|
| `spec/userstories.md` | Ralph | Read and write |
| `spec/setup-tasks.md` | Ralph | Read and write |
| `spec/personas.md` | Ralph | Read and write |
| `{{RUN_DIR}}/progress.txt` | Ralph | Read and append |
| `{{RUN_DIR}}/questions.md` | Ralph | Read and write |
| `{{RUN_DIR}}/answers.md` | Human | **Read only.** Only remove entries you have already processed. |
| `{{RUN_DIR}}/overseer.md` | Overseer | **Read only.** Never modify. |
| `Ideen.md` | Human | **Read only.** Never modify. |
| `CLAUDE.md` | Human | **Read only.** Never modify. |
## Handling Uncertainty
You MUST NOT make architectural or design decisions on your own. Check the "Architekturentscheidungen die NOCH NICHT getroffen wurden" section in `Ideen.md`.
- **If a question affects the core of a story** (answer could require a rewrite): Do NOT change the story. Add a question to `{{RUN_DIR}}/questions.md` instead.
- **If a question only affects a detail** (only individual criteria would change): Mark it with `⚠️ ASSUMPTION:` in the Notes section and add a question to `{{RUN_DIR}}/questions.md`.
### Question Format
```
### Q-{number}: {short title}
**Context:** {why this question came up}
**Affects:** {which stories/tasks are affected}
**Options:** {possible answers, if applicable}
```
## Rules
- ONE task per iteration. No exceptions.
- Derive all content strictly from `Ideen.md`. Do not invent features.
- Respect binding decisions in `Ideen.md` ("Getroffene Designentscheidungen").
- Respect project statutes in `CLAUDE.md`.
- Keep stories small and focused. One story = one user-facing capability.
- Acceptance criteria must be concrete and testable.
- Write everything in English (per statutes: code and documentation in English).
- Use the correct persona roles: "event organizer" vs "guest" vs "self-hoster" vs "user" (when both roles apply).
- After modifying any story, update the status counters at the top of `spec/userstories.md`.
- Always append a summary of your single task to `{{RUN_DIR}}/progress.txt` before stopping.

View File

@@ -0,0 +1,7 @@
# Run: story-refinement
- **Created**: 2026-03-02
- **Description**: Initial user story refinement — reviewing and improving all user stories and setup tasks for implementation readiness.
- **Model**: opus
- **Max iterations**: 20
- **Status**: Completed (29 iterations)

View File

@@ -0,0 +1,11 @@
# Overseer Notes
<!-- This file is written by the overseer session. Ralph reads it but never modifies it. -->
## Action Required
(No action items — this run is archived.)
## Observations
(No observations.)

View File

@@ -0,0 +1,160 @@
# Ralph Loop Progress Log
# Each iteration appends its findings and decisions here.
# Previous phase (story writing) completed with 17 user stories and 3 setup tasks.
# Full history archived. Starting fresh for refinement phase.
## Iteration 1 — US-1 reviewed
- Added missing honeypot field acceptance criterion to the event creation form, consistent with Ideen.md's "Honeypot-Felder in Formularen" and US-3's existing honeypot criterion. Event creation is an open, unauthenticated form and needs the same bot protection.
- Clarified that the server returns BOTH the event token and the organizer token in the creation response (was ambiguous — criterion said organizer token is "stored in localStorage" without explicitly stating the server returns it).
## Iteration 2 — US-2 reviewed
- Reviewed US-2 (View event landing page), no changes needed. Token terminology is correct, all criteria are concrete and testable, edge cases (expired event, event not found, zero attendees) are covered, no external resources requirement is present, and scope is appropriate as the base event page with other stories adding their own elements independently.
## Iteration 3 — US-3 reviewed
- Fixed a cross-story consistency issue in the localStorage data AC: US-1 and US-6 both explicitly store "event token, title, and date" in localStorage, but US-3 only mentioned "event title and date" alongside the RSVP data, omitting the event token. Added the event token to the AC for consistency. The event token is needed so US-7 can link back to the event page. All other aspects of US-3 are solid: token terminology correct, Ideen.md alignment verified (RSVP flow, honeypot, duplicate protection, acceptable spam risk), scope appropriate, all ACs testable, edge cases covered (pre-fill, update existing entry, expiry block).
## Iteration 4 — US-4 reviewed
- Reviewed US-4 (Manage guest list as organizer), no changes needed. Token terminology correct (organizer token, localStorage). All 7 ACs are concrete and testable. Edge cases covered: access control (token present/absent), deletion with immediate update, no guessable URL. Anonymous "not attending" entries (US-3 allows optional name) are implicitly covered by "all RSVPs." Expiry behavior is consistent — US-4 doesn't restrict by expiry, organizer view works as long as data exists, US-12 handles cleanup. Cross-references to US-1 and Q-4 are accurate. Scope appropriate (separate from US-5 editing). Aligned with Ideen.md ("Einsicht angemeldete Gäste, kann bei Bedarf Einträge entfernen") and persona capabilities.
## Iteration 5 — US-5 reviewed
- Added `**Dependencies:** US-1` field. US-5 requires an existing event and the organizer token in localStorage, both established by US-1. References to US-12 (expiry cleanup) and US-9 (change highlighting) are informational cross-references, not runtime dependencies.
- All other aspects verified and solid: token terminology correct, editable fields match US-1 creation fields exactly, no honeypot needed (form is already behind organizer token auth), expiry manipulation edge case covered with confirmation warning, all 9 ACs are concrete and testable, scope appropriate (separate from US-4 guest management, US-9 change highlighting, US-10 update messages), aligned with Ideen.md and Q-4 resolution.
- Noted for later: when the organizer edits title/date, the locally cached values in localStorage (used by US-7's overview) should be refreshed. This is already acknowledged in US-7's notes as "refreshed when the user next visits the event page." The specific refresh mechanism will be addressed during US-7 review.
## Iteration 6 — US-6 reviewed
- Added `**Dependencies:** US-2` field. US-6 places a bookmark action on the event page (US-2), which provides both the UI surface and the event data (token, title, date) that gets stored in localStorage. No direct dependency on US-1 (that's transitive through US-2).
- All other aspects verified and solid: token terminology correct, localStorage data format consistent with US-1 and US-3 ("event token, event title, and event date"), edge cases covered (expired events, toggle on/off, RSVP independence, no server contact), all 7 ACs concrete and testable, scope appropriate, aligned with Ideen.md ("rein lokal, kein Serverkontakt, kein Name nötig") and privacy statutes.
- Note: US-1 through US-4 were reviewed before the Dependencies convention was established in iteration 5. Missing Dependencies fields for those stories should be addressed during cross-cutting review.
## Iteration 7 — Overseer action item: Event cancellation as explicit feature
- Addressed the overseer's action item: "Event cancellation must be an explicit feature, not a side effect of editing the expiry date."
- Created US-18 (Cancel an event as organizer) with 12 acceptance criteria covering: dedicated cancel action, optional cancellation message, optional expiry adjustment, confirmation step, cancelled state display, RSVP blocking, one-way state transition (no un-cancel), organizer token auth.
- Updated US-5: Removed cancellation-via-expiry framing. The confirmation warning for setting expiry to today/past now explains data deletion consequences without implying cancellation. Notes updated to reference US-18 as the dedicated cancellation mechanism.
- Updated US-2: Added new AC for cancelled state display (shows "cancelled" with cancellation message, no RSVP actions). Updated notes to explain cancelled-state requirement.
- Updated US-3: Added new AC blocking RSVP submission on cancelled events (references US-18).
- Updated personas.md: Event Organizer capabilities now list US-5 (edit) and US-18 (cancel) separately instead of the old "canceling by shortening expiry (US-5)". Added US-18 to Appears in.
- Updated status counter: 17 → 18 total stories, 17 → 18 remaining.
## Iteration 8 — US-7 reviewed
- Added `**Dependencies:** None` field. US-7 lives at the root page `/` and renders entirely from localStorage. It reads data written by US-1 (organizer tokens), US-3 (RSVP records), and US-6 (bookmarks), but handles the absence of all data gracefully with an explicit empty state (AC 8). No other story's runtime output is required for US-7 to function.
- All other aspects verified and solid: token terminology correct, localStorage data format consistent with US-1/US-3/US-6 (all store event token, title, date), cross-references accurate, all 11 ACs concrete and testable, edge cases well covered (empty state, expired events, deleted events, entry removal for all three sources, confirmation for organizer token loss). Cancelled events are server-side state — US-7 is purely client-side and correctly doesn't attempt to display cancellation status (US-2 handles that when the user clicks through). Scope appropriate for one implementation effort. Aligned with Ideen.md and Q-2 resolution.
## Iteration 9 — US-8 reviewed
- Added `**Dependencies:** US-2` field. US-8 places .ics download and webcal:// subscription links on the event page (US-2), which provides the UI surface and the event data to generate calendar entries.
- Added a new AC for cancelled events: when an event is cancelled (US-18), the .ics file and webcal:// feed must include `STATUS:CANCELLED` so subscribed calendar applications reflect the cancellation on their next sync. This is especially important for webcal:// subscribers — the entire point of the subscription is live updates, and cancellation is the most critical update a calendar subscriber needs to see.
- All other aspects verified and solid: token terminology correct (event token used for UID derivation and URL), all 9 ACs (now 10) concrete and testable, scope appropriate, no overlap with other stories, aligned with Ideen.md ("Kalender-Integration: .ics-Download + optional webcal:// für Live-Updates bei Änderungen") and privacy statutes (server-side generation, no external services, no PII logging).
## Iteration 10 — Overseer action item: Explicit event deletion feature
- Addressed the overseer's second action item: "Explicit event deletion feature."
- Created US-19 (Delete an event as organizer) with 9 acceptance criteria covering: dedicated delete action (separate from edit and cancel), confirmation warning about irreversibility, permanent deletion of all associated data (RSVPs, update messages, field-change metadata, header images, cancellation state), organizer token auth, works regardless of event state (active/cancelled/expired).
- Updated US-5: Removed the two ACs that allowed setting expiry to today/past with a confirmation warning. Replaced with a single AC enforcing that the expiry date can only be set to a future date, with a validation message directing to US-19 for immediate deletion. Updated notes to explain the rationale.
- Updated personas.md: Event Organizer capabilities now list US-19. Added US-19 to "Appears in."
- Updated status counter: 18 → 19 total stories, 18 → 19 remaining.
## Iteration 11 — US-9 reviewed
- Added `**Dependencies:** US-2, US-5` field. US-9 requires the event page (US-2) as its display surface where highlights are rendered, and US-5 (edit event details) as the trigger that causes the server to record which fields changed and when. Without US-5, there are no edits to highlight.
- All other aspects verified and solid: token terminology correct, localStorage key `last_seen_at` is distinct from US-10's `updates_last_seen_at`, all 8 ACs concrete and testable, edge cases covered (first visit, multiple successive edits, no edits since creation), scope appropriate, no overlap with US-10 (free-form messages vs. field change highlighting), aligned with Ideen.md and privacy statutes (no visit data transmitted to server).
## Iteration 12 — US-10 reviewed
- Added `**Dependencies:** US-1, US-2` field. US-10 requires US-1 for event creation and the organizer token in localStorage (needed to access the organizer view and post messages), and US-2 for the public event page where update messages are displayed to guests.
- Added explicit note about cancelled event behavior: posting update messages is not blocked by cancellation, only by expiry (AC 4). This is intentional — US-18 explicitly blocks RSVPs and allows editing the cancellation message, while update messages serve a different purpose (stream of announcements vs. static cancellation reason). The organizer may need to communicate post-cancellation (e.g. rescheduling info).
- All other aspects verified and solid: token terminology correct, localStorage key `updates_last_seen_at` distinct from US-9's `last_seen_at`, cross-references accurate, all 11 ACs concrete and testable, scope appropriate, no overlap with US-9 (free-form messages vs. field change highlighting), aligned with Ideen.md and privacy statutes.
## Iteration 13 — US-11 reviewed
- Added `**Dependencies:** US-2` field. US-11 places the QR code on the event page (US-2), which provides both the display surface and the public event URL that the QR code encodes.
- All other aspects verified and solid: token terminology correct ("public event URL", "event link"), all 7 ACs concrete and testable, server-side generation explicitly required (no external services — consistent with privacy statutes), cancelled event behavior doesn't need special QR handling (QR encodes the URL, US-2 handles cancelled display when scanned), expired event QR availability explicitly covered (AC 7), scope appropriate, no overlap with other stories, aligned with Ideen.md ("QR Code generieren") and persona capabilities (both organizer and guest have QR code access in personas.md).
## Iteration 14 — US-12 reviewed
- Added `**Dependencies:** US-1` field. US-12 is a background cleanup process that deletes expired events. It requires events to exist (created by US-1). The AC references data from US-3 (RSVPs), US-9 (field-change metadata), US-10 (update messages), and US-16 (header images), but US-12 doesn't structurally depend on those stories — it cascades whatever associated data exists. If those features aren't implemented yet, there's simply nothing extra to cascade.
- All other aspects verified and solid: token terminology correct, all 7 ACs concrete and testable, edge cases covered (extending expiry before it passes via US-5, post-deletion URL behavior, no PII logging during deletion, timing guarantee). Cancelled events (US-18) still have an expiry date and are cleaned up normally — the cancellation state is part of the event record and is implicitly deleted. No overlap with US-19 (manual deletion vs. automatic cleanup). Aligned with Ideen.md ("Ablaufdatum als Pflichtfeld, nach dem alle gespeicherten Daten gelöscht werden") and privacy statutes.
## Iteration 15 — US-13 reviewed
- Added `**Dependencies:** US-1` field. US-13 enforces a server-side limit on event creation — it modifies the event creation flow (US-1) by rejecting new events when the cap is reached. Without the event creation endpoint, there is nothing to apply the limit against.
- All other aspects verified and solid: no token terminology involved (server config feature), ACs are concrete and testable, edge cases covered (expired events excluded from count, unlimited default when env var unset, server-side enforcement). Cancelled events (US-18) correctly count toward the limit since they still consume server resources and remain accessible until expiry — this is self-evident from the "non-expired" criterion. Deleted events (US-19) obviously don't count since the record is removed. No overlap with other stories. Aligned with Ideen.md ("Max aktive Events als serverseitige Konfiguration (env variable)") and CLAUDE.md deployment model.
## Iteration 16 — US-14 reviewed
- Added `**Dependencies:** None` field. US-14 is PWA infrastructure (manifest, service worker, icons) that can be set up independently. The start_url reference to `/` (US-7's content) is informational — the PWA shell doesn't structurally depend on the content at the root page.
- All other aspects verified and solid: no token terminology involved, all 7 ACs concrete and testable, service worker caching framed as "fast loading" without over-promising offline functionality (appropriate for SPA + REST API), no external resources requirement consistent with privacy statutes, start URL and SPA references match Q-2 and Q-3 resolutions, scope appropriate for single implementation effort, no overlap with other stories. Aligned with Ideen.md ("Soll als PWA im Browser laufen").
## Iteration 17 — US-15 reviewed
- Added `**Dependencies:** US-1, US-2` field. US-15 adds a theme picker to the event creation form (US-1) and renders the selected theme on the event page (US-2). Both are structurally required at runtime. US-5 (editing) is referenced as a secondary entry point for changing the theme but is not a strict runtime dependency — US-15 works for newly created events without US-5.
- Added a note about the dark/light mode interaction: predefined themes must remain readable and visually coherent regardless of whether the user has dark or light mode active. This addresses the overseer's observation about potential visual conflicts between US-15 and US-17, and cross-references the matching note in US-17.
- All other aspects verified and solid: token terminology not applicable, all 7 ACs concrete and testable, default theme fallback covered (AC 2), scope appropriate, no overlap with US-16 (header images) or US-17 (dark/light mode), aligned with Ideen.md and Q-1 resolution.
## Iteration 19 — US-17 reviewed
- Added `**Dependencies:** None` field. US-17 is purely client-side UI infrastructure (CSS theming + localStorage toggle for dark/light mode preference). It doesn't structurally depend on any other story's runtime output.
- All other aspects verified and solid: no token terminology involved, all 8 ACs concrete and testable, scope boundary with US-15 is explicitly delineated (AC 5-6: app-level dark/light mode vs. event-level color themes), no overlap with other stories, cross-reference to US-15 in Notes is accurate and reciprocal (US-15 notes reference US-17 interaction, added in iteration 17). WCAG AA contrast requirement aligns with CLAUDE.md accessibility statute. Scope appropriate for single implementation effort.
- Alignment note: US-17 doesn't trace to an explicit Ideen.md feature (no mention of dark/light mode), but is consistent with the PWA "feels like a native app" goal and CLAUDE.md's "accessibility is a baseline requirement" statute. No action needed — the story was established during the writing phase and aligns with project principles.
## Iteration 20 — US-18 reviewed
- Added "must be in the future" constraint to AC 3 (optional expiry date adjustment during cancellation). The overseer's directive explicitly included this constraint, but the AC as written only described the purpose ("to control how long the cancellation notice remains visible") without stating the rule. Now consistent with US-5's future-only expiry enforcement and US-19's role as the immediate-removal mechanism.
- All other aspects verified and solid: Dependencies field present (US-1), token terminology correct, all 12 ACs concrete and testable, cross-references verified against US-2/US-3/US-5/US-8/US-19/personas.md, scope appropriate (single cohesive capability), no overlap with US-19, edge cases covered (one-way state transition, organizer auth, editable cancellation message post-cancellation).
## Iteration 18 — US-16 reviewed
- Added `**Dependencies:** US-1, US-2` field. US-16 integrates into the event creation form (US-1) as the primary entry point for selecting a header image, and displays the selected image on the event page (US-2). Both are structurally required at runtime. US-5 is referenced as a secondary entry point for changing the image during editing but is not a strict runtime dependency — US-16 works for newly created events without US-5 (same pattern as US-15).
- All other aspects verified and solid: token terminology correct (event token referenced indirectly through US-12 cleanup), all 11 ACs concrete and testable, edge cases well covered (no API key graceful degradation, API key removed after images stored, cleanup on expiry, persistent volume requirement). Cancelled events don't need special handling — the header image continues to display since the event page still renders per US-18/US-2. No overlap with US-15 (color themes vs. header images). Aligned with Ideen.md (Q-1 resolution), CLAUDE.md privacy statutes (server-proxied Unsplash), and personas (organizer capability). Consistent with US-12 and US-19 which both reference "stored header images (US-16)" for deletion cascading.
## Iteration 21 — US-19 reviewed
- Added a new AC for post-deletion UI behavior: after the server confirms deletion, the app must remove the event's organizer token and metadata from localStorage and redirect the organizer to the root page (`/`). Without this, the organizer would be stranded on a broken page with stale localStorage data. This follows the same pattern as the confirmation/cleanup behavior in US-7 (AC 9-10) but is proactive rather than reactive — the organizer triggered the deletion, so the app should handle cleanup immediately rather than waiting for the next visit.
- All other aspects verified and solid: Dependencies field already present (US-1) — correct, the organizer token established by US-1 is the structural requirement. Token terminology correct. All 10 ACs (now 10, was 9) concrete and testable. Edge cases covered: works regardless of event state (active/cancelled/expired), confirmation warning, no PII logging. Cross-references to US-5, US-18, US-2, US-12, US-9, US-10, US-16 are accurate. Scope appropriate (single capability, distinct from US-18 cancel). No overlap with other stories. Aligned with privacy statutes and overseer directive.
- Noted for cross-cutting review: US-7 AC 7 parenthetical "(deleted per US-12)" should also reference US-19, since manual organizer deletion is an equally valid reason an event might return "not found" from the server.
## Iteration 22 — T-1 reviewed
- Added `**Dependencies:** None` field.
- Added GPL LICENSE file to AC 4 (shared top-level files). CLAUDE.md explicitly states the project is GPL-licensed — a repository scaffold should include the license file. The previous list (README, Dockerfile, CLAUDE.md, .gitignore) omitted it.
- All other aspects verified and solid: tech stack matches Ideen.md exactly (Java latest LTS, Spring Boot, Maven, hexagonal/onion architecture, Svelte with Vite), the Dockerfile placeholder in T-1 is reasonable alongside T-2 which gives it real multi-stage build content, .gitignore coverage is appropriately scoped for both Java/Maven and Node/Svelte ecosystems, "both projects build successfully with no source code" is a clear, testable criterion for the empty scaffold.
## Iteration 23 — T-2 reviewed
- Added `**Dependencies:** T-1` field. T-2's multi-stage Dockerfile builds from the `backend/` and `frontend/` directories scaffolded by T-1, and the README it documents in is also created by T-1.
- All other aspects verified and solid: ACs are concrete and testable, PostgreSQL via env var consistent with Ideen.md ("PostgreSQL, wird separat gehostet... verbindet sich per Konfiguration (env variable) mit der externen Postgres-Instanz"), runtime env vars align with US-13 (MAX_ACTIVE_EVENTS) and US-16 (Unsplash API key), docker-compose example aligns with CLAUDE.md deployment statute ("docker-compose example in the README is sufficient"), health-check endpoint is standard Docker practice. The "migrations run on startup or are documented" phrasing correctly leaves the implementation strategy open. No overlap with T-1 (T-1 creates a placeholder Dockerfile, T-2 gives it real multi-stage build content — already noted in iteration 22). US-16's persistent volume requirement will be added to the docker-compose example when US-16 is implemented — not T-2's scope.
## Iteration 24 — T-3 reviewed
- Added `**Dependencies:** T-1, T-2` field. T-3 needs T-1 for the repository structure (both projects to test/build) and T-2 for the Dockerfile (used by the pipeline to build and publish the container image).
- Identified an undocumented assumption: T-3 originally assumed Gitea as the CI/CD platform (Gitea Actions, `.gitea/workflows/`, Gitea container registry). This choice is not documented in Ideen.md or CLAUDE.md. Generalized the task title and ACs to be platform-independent (removed "Gitea" from title and AC references), flagged with ⚠️ ASSUMPTION in Notes, and raised Q-5 for the human to decide on the CI/CD platform.
- Core ACs (test, build, publish on success, fail visibly on errors) are solid, concrete, and testable regardless of platform choice. The platform-specific details (workflow format, registry config) will be filled in once Q-5 is resolved.
## Iteration 25 — Cross-cutting review: deferred consistency fixes
- Added `**Dependencies:** None` to US-1 (foundational story, no dependencies).
- Added `**Dependencies:** US-1` to US-2 (requires an event to exist for display).
- Added `**Dependencies:** US-2` to US-3 (RSVP form is placed on the event page).
- Added `**Dependencies:** US-1` to US-4 (requires organizer token and event existence; consistent with US-5's dependency pattern).
- These four stories were reviewed before the Dependencies convention was established in iteration 5 (noted in iteration 6 for cross-cutting resolution).
- Fixed US-7 AC 7: changed "(deleted per US-12)" to "(deleted per US-12 or US-19)" — manual organizer deletion (US-19) is an equally valid reason an event might return "not found" (noted in iteration 21 for cross-cutting resolution).
- All 19 stories and 3 tasks now have Dependencies fields. All deferred items from individual reviews are resolved.
## Iteration 26 — Cross-cutting review #1: Ideen.md feature coverage
- Systematically mapped every feature, requirement, and design decision from Ideen.md to user stories and setup tasks.
- **Grundsätze**: PWA (US-14), self-hostable (T-2, US-13), privacy (embedded across all stories), no registration/login (US-1, US-3), localStorage for local data (US-1, US-3, US-6, US-7, US-9, US-10, US-17). ✓
- **Core idea/Zielbild**: Event creation (US-1), sharing via link (US-1/US-2), RSVP (US-3). ✓
- **Gedankensammlung features**: Landing page (US-2), one link per event (US-1/US-2), designable (US-15/US-16), RSVP with name + server+localStorage (US-3), duplicate protection (US-3), bookmark/follow (US-6), organizer update (US-5), organizer guest management (US-4), calendar .ics+webcal (US-8), change highlighting (US-9), update messages (US-10), QR code (US-11), mandatory expiry + data deletion (US-1/US-12), localStorage overview (US-7). ✓
- **Sicherheit/Missbrauchsschutz**: Non-guessable UUIDs (US-1), open creation (US-1), max events config (US-13), honeypot fields (US-1/US-3). ✓
- **Abgrenzungskriterien**: No chat, no discovery (US-1 AC), no event planning — all correctly absent. ✓
- **Getroffene Designentscheidungen**: Single link per event, RSVP name, localStorage binding, spam=acceptable risk, no rate limiting/captcha/admin password (correctly absent), pragmatic measures (covered), SPA+REST (T-1/Q-3), PostgreSQL external (T-2), two UUIDs (US-1/token model), single Docker container (T-2), Java LTS+Spring Boot+Maven+hexagonal (T-1), Svelte+Vite (T-1). ✓
- **Overseer-added features**: Event cancellation (US-18), event deletion (US-19). ✓
- **Note**: US-17 (dark/light mode) has no direct Ideen.md entry but aligns with PWA "native app feel" and CLAUDE.md accessibility statute (already noted in iteration 19).
- **Result**: All features from Ideen.md are fully covered. No gaps found.
## Iteration 27 — Cross-cutting review #2: Persona consistency
- Verified all three personas (Event Organizer, Guest, Self-Hoster) against the actual stories.
- **Finding**: US-7 (local event overview) and US-17 (dark/light mode) are both written "As a user" (both roles apply), but the Event Organizer persona was missing these from both capabilities and "Appears in." The Guest persona already had them.
- **Fix**: Added US-7 and US-17 to Event Organizer's capabilities (with "(shared with Guest — story uses 'user' role)" annotation) and to the "Appears in" list.
- US-14 (PWA install) is "As a guest" — the organizer also benefits from PWA but the story is written from the guest perspective and the organizer doesn't have unique PWA interactions. Left as-is.
- Guest persona: all capabilities, limitations, and "Appears in" entries verified correct. ✓
- Self-Hoster persona: capabilities (US-13, US-16 config, T-2 deployment) and limitations all verified correct. ✓
- Remaining cross-cutting checks: status counter accuracy, cross-story inconsistencies.
## Iteration 28 — Cross-cutting review #3: Status counter accuracy
- Verified the status counter at the top of `userstories.md`: Total 19, Complete 0, Remaining 19.
- Counted all stories in the file: US-1 through US-19 = 19 stories. No stories have been implemented yet.
- Status counter is accurate. No changes needed.
- Remaining cross-cutting check: cross-story inconsistencies.
## Iteration 29 — Cross-cutting review #4: Cross-story inconsistencies
- Systematically checked localStorage keys, token terminology, expiry behavior, cancellation behavior, deletion cascading, cross-references, and dependency chains across all 19 stories and 3 tasks.
- **Fix 1 — US-12 AC 2**: Added "(US-10)" cross-reference after "update messages" and added "cancellation state (US-18 if applicable)" to the deletion cascade list. Now matches the explicit enumeration in US-19 AC 3, which describes the same cascading deletion behavior. Previously missing because US-12 was reviewed (iteration 14) before US-18 and US-19 existed (created in iterations 7 and 10).
- **Fix 2 — US-12 AC 3**: Removed "or 'event has ended'" from the post-deletion response. After the cleanup process deletes event data, the server cannot distinguish "this event was cleaned up" from "this event never existed" — the only honest response is "event not found," consistent with US-2 AC 6 and US-19 AC 4. The "event has ended" display (US-2 AC 4) applies only during the window when an event is expired but not yet cleaned up — that happens before US-12 acts, not after.
- **Fix 3 — US-7 AC 6**: Changed "Events that have expired" to "Events whose date has passed." The expiry date is not stored in localStorage (US-1, US-3, US-6 all store event token, title, and event date — not the expiry date), and US-7 AC 5 requires the list to render entirely from localStorage. The event date — which IS stored locally — is what matters to users: whether the event has already taken place, not the server's data retention deadline.
- **No other cross-story inconsistencies found.** All localStorage key names consistent, token terminology consistent, expiry/cancellation behavior consistent, cross-references accurate, dependency chains valid with no cycles.
- All four cross-cutting checks now complete: (1) Ideen.md feature coverage ✓, (2) Persona consistency ✓, (3) Status counter accuracy ✓, (4) Cross-story inconsistencies ✓.

View File

@@ -0,0 +1,12 @@
# Questions
<!-- Questions raised during the story-refinement run. -->
<!-- This run is archived — see progress.txt for details. -->
## Open
(No open questions.)
## Resolved
(Questions from this run were carried forward into review-findings.)

View File

@@ -0,0 +1 @@
=== Run completed (archived) | model=opus | max=20 | iterations=29 ===

View File

@@ -0,0 +1,3 @@
# Answers
<!-- Human answers to open questions. Ralph processes these one per iteration. -->

View File

@@ -0,0 +1,97 @@
# Ralph Loop — Verify Implementation Order
You are an autonomous agent running inside a Ralph Loop. Your job is to verify that the implementation order documented in `spec/implementation-phases.md` is consistent with the declared dependencies in all user stories and setup tasks. You report findings — you do not modify any spec files.
## CRITICAL RULE: One Task Per Iteration
You MUST perform exactly ONE task per iteration. Not two, not "a few small ones", not "all remaining items". ONE.
After completing your single task:
1. Append a short summary of what you did to `{{RUN_DIR}}/progress.txt`.
2. Stop. Do not look for more work. Do not "while I'm at it" anything.
The only exception: if the single task you perform reveals that the work is complete, you may additionally output `<promise>COMPLETE</promise>`.
## Startup: Read Project State
At the start of every iteration, read these files in order:
1. `{{RUN_DIR}}/progress.txt` — what previous iterations did (your memory across iterations).
2. `{{RUN_DIR}}/overseer.md` — notes from the overseer. Items under `## Action Required` have highest priority.
3. `{{RUN_DIR}}/answers.md` — check if the human answered any open questions.
4. `{{RUN_DIR}}/questions.md` — open and resolved questions.
5. `CLAUDE.md` — project statutes and principles.
6. `spec/implementation-phases.md` — the implementation order you are verifying.
7. `spec/userstories.md` — user stories with their declared dependencies.
8. `spec/setup-tasks.md` — setup tasks with their declared dependencies.
9. `Ideen.md` — project vision and binding design decisions.
## Task Selection (Priority Order)
Pick the FIRST applicable task from this list. Do that ONE task, then stop.
### Priority 1: Overseer action items
If `{{RUN_DIR}}/overseer.md` has items under `## Action Required`, address the FIRST one that hasn't been addressed yet (check `{{RUN_DIR}}/progress.txt`). Do NOT modify `{{RUN_DIR}}/overseer.md`.
### Priority 2: Process answers
If `{{RUN_DIR}}/answers.md` contains an answer, process it. Remove the processed entry from `{{RUN_DIR}}/answers.md`.
### Priority 3: Verify one phase
Work through the phases in `spec/implementation-phases.md` in order (Phase 0, Phase 1, Phase 2, ...). For each phase, check:
- **Dependency satisfaction:** Every story/task in this phase must have all its declared dependencies satisfied by stories/tasks in earlier phases (or the same phase, if they are independent of each other within the phase). Flag any story that is scheduled before one of its dependencies.
- **Completeness:** Are there stories/tasks whose dependencies are all satisfied by earlier phases but that are not yet scheduled in any phase? They could be moved earlier.
- **Parallelizability:** Are stories within the same phase truly independent of each other, or do some have inter-dependencies that force a specific order within the phase?
Report findings in `{{RUN_DIR}}/progress.txt`. If you find a problem, also raise it as a question in `{{RUN_DIR}}/questions.md` with a suggested fix.
Track which phases you have verified in `{{RUN_DIR}}/progress.txt` so you don't re-verify them.
### Priority 4: Cross-cutting consistency check
After all individual phases have been verified, do ONE cross-cutting check:
- Are all user stories and setup tasks from `spec/userstories.md` and `spec/setup-tasks.md` accounted for in some phase?
- Are there circular dependencies that make the order impossible?
- Does the phase grouping align with the practical notes in `Ideen.md` (e.g. vertical slice considerations, MVP scope)?
- Is the overall order sensible from a development workflow perspective (e.g. infrastructure before features, backend before frontend where needed)?
Report findings in `{{RUN_DIR}}/progress.txt`.
### Priority 5: Complete
If all phases are verified, the cross-cutting check is done, all answers processed, and all overseer action items addressed:
Output `<promise>COMPLETE</promise>` and stop.
## File Ownership
Respect these boundaries strictly:
| File | Owner | You may... |
|------|-------|------------|
| `{{RUN_DIR}}/progress.txt` | Ralph | Read and append |
| `{{RUN_DIR}}/questions.md` | Ralph | Read and write |
| `{{RUN_DIR}}/answers.md` | Human | **Read only.** Only remove entries you have already processed. |
| `{{RUN_DIR}}/overseer.md` | Overseer | **Read only.** Never modify. |
| `CLAUDE.md` | Human | **Read only.** Never modify. |
| `spec/implementation-phases.md` | Human | **Read only.** Never modify. |
| `spec/userstories.md` | Human | **Read only.** Never modify. |
| `spec/setup-tasks.md` | Human | **Read only.** Never modify. |
| `Ideen.md` | Human | **Read only.** Never modify. |
## Handling Uncertainty
If you encounter an ambiguity in the dependency declarations (e.g. a dependency that could be interpreted multiple ways, or a missing dependency that seems implied but isn't declared), raise a question in `{{RUN_DIR}}/questions.md`:
```
### Q-{number}: {short title}
**Context:** {why this question came up}
**Affects:** {which stories/tasks/phases are affected}
**Suggested fix:** {what you think should change in implementation-phases.md}
```
## Rules
- ONE task per iteration. No exceptions.
- You are a verifier, not an editor. Never modify spec files.
- Base your analysis on declared dependencies (`**Dependencies:**` fields), not on your own judgment of what should depend on what.
- When you find issues, be specific: name the story/task, the phase it's in, the dependency that is violated, and where the dependency should be satisfied.

View File

@@ -0,0 +1,6 @@
# Run: verify-impl-order
- **Created**: 2026-03-03
- **Description**: (add description here)
- **Model**: sonnet
- **Max iterations**: 10

View File

@@ -0,0 +1,11 @@
# Overseer Notes
<!-- This file is written by the overseer session. Ralph reads it but never modifies it. -->
## Action Required
(No action items.)
## Observations
(No observations.)

View File

@@ -0,0 +1,189 @@
# Ralph Loop Progress Log
# Each iteration appends its findings and decisions here.
---
## Iteration 1 — 2026-03-03
**Task:** Verify Phase 0 (Project Infrastructure)
**Verified phases:** Phase 0
**Method:** Checked each task's position in Phase 0 against its declared `Dependencies:` field in spec/setup-tasks.md.
### Findings
**Dependency satisfaction:** PASS
| Task | Declared Dependencies | Scheduled at | All deps in earlier slots? |
|------|-----------------------|--------------|---------------------------|
| T-1 | None | Order 1 | ✓ (no deps) |
| T-2 | T-1 | Order 2 | ✓ T-1 is Order 1 |
| T-4 | T-1, T-2 | Order 3 | ✓ T-1 (Order 1), T-2 (Order 2) |
| T-3 | T-1, T-2 | Order 3* | ✓ T-1 (Order 1), T-2 (Order 2) |
**Completeness:** PASS — all four setup tasks (T-1, T-2, T-3, T-4) are accounted for in Phase 0.
**Parallelizability:** PASS — T-3 and T-4 have identical declared dependencies (T-1, T-2) and are marked as parallelizable with each other. This is correct.
**Minor prose inconsistency (not an ordering problem):** Phase 0's introductory sentence states "All setup tasks must complete before any user story work begins," but the same paragraph notes "T-3 can run in parallel with Phase 1." These two sentences contradict each other in plain language. The dependency-based analysis is correct: no user story has a declared dependency on T-3, so Phase 1 can proceed without T-3 being complete. The parenthetical note is accurate; the introductory sentence is overly broad. Not raising a question since this is a prose issue only and the implementation order is unambiguous.
**Verdict:** Phase 0 is correctly ordered. No dependency violations found.
---
## Iteration 3 — 2026-03-03
**Task:** Verify Phase 2 (Organizer Management & Event Lifecycle)
**Verified phases:** Phase 0, Phase 1, Phase 2
**Method:** Checked each story's position in Phase 2 against its declared `Dependencies:` field in spec/userstories.md. Phases 0 and 1 are treated as already verified.
### Findings
**Dependency satisfaction:** PASS
| Story | Declared Dependencies | All deps in earlier phases? |
|-------|-----------------------|-----------------------------|
| US-4 | US-1, T-4 | ✓ US-1 is Phase 1; T-4 is Phase 0 |
| US-5 | US-1, T-4 | ✓ US-1 is Phase 1; T-4 is Phase 0 |
| US-18 | US-1, T-4 | ✓ US-1 is Phase 1; T-4 is Phase 0 |
| US-19 | US-1, T-4 | ✓ US-1 is Phase 1; T-4 is Phase 0 |
| US-12 | US-1, T-4 | ✓ US-1 is Phase 1; T-4 is Phase 0 |
| US-13 | US-1, T-4 | ✓ US-1 is Phase 1; T-4 is Phase 0 |
**Completeness:** PASS — All six stories with dependencies satisfied by Phase 1 or earlier (and belonging to the organizer/lifecycle domain) are accounted for in Phase 2. No eligible story is missing.
Note: Several later-phase stories (US-6, US-8, US-10a, US-11, US-15, US-16, US-7, US-14, US-17) also have their declared dependencies satisfied by Phase 1 or earlier, but they are correctly grouped into later phases by functional domain (enhanced guest features, visual customization, PWA shell). This is a deliberate, documented grouping choice — not a scheduling error.
**Parallelizability:** PASS — All six Phase 2 stories declare only US-1 and T-4 as dependencies. None of them declare a dependency on any other Phase 2 story. They are structurally independent and can be worked on in parallel. The recommended within-phase order (US-5 first, since US-9 in Phase 3 depends on it) is an efficiency hint, not a structural constraint.
**Undeclared dependency observation (no violation):** US-4's ACs state the organizer view is "accessible from the event page" — this implies UX integration with US-2 at the view level. However, the organizer view can be implemented as a standalone route/endpoint independently of US-2's visual implementation, so the omission of US-2 from US-4's declared dependencies is defensible and consistent with Phase 2 placement. No ordering violation.
**Mermaid diagram consistency:** The diagram shows US-1 branching to all Phase 2 stories (US-4, US-5, US-12, US-13, US-18, US-19). T-4 is not shown as a direct arrow to these stories, but it is a transitive dependency via T-4 → US-1. The diagram caption notes "(and T-4 transitively)" — consistent with declared dependencies.
**Verdict:** Phase 2 is correctly ordered. No dependency violations found.
---
## Iteration 4 — 2026-03-03
**Task:** Verify Phase 3 (Enhanced Event Page Features)
**Verified phases:** Phase 0, Phase 1, Phase 2, Phase 3
**Method:** Checked each story's position in Phase 3 against its declared `Dependencies:` field in spec/userstories.md. Phases 02 are treated as already verified.
### Findings
**Dependency satisfaction:** PASS
| Story | Declared Dependencies | All deps in earlier phases? |
|-------|-----------------------|-----------------------------|
| US-6 | US-2, T-4 | ✓ US-2 is Phase 1; T-4 is Phase 0 |
| US-8 | US-2, T-4 | ✓ US-2 is Phase 1; T-4 is Phase 0 |
| US-9 | US-2, US-5, T-4 | ✓ US-2 is Phase 1; US-5 is Phase 2; T-4 is Phase 0 |
| US-10a | US-1, US-2, T-4 | ✓ US-1 and US-2 are Phase 1; T-4 is Phase 0 |
| US-10b | US-10a | US-10a is Phase 3 (same phase) — see parallelizability below |
| US-11 | US-2, T-4 | ✓ US-2 is Phase 1; T-4 is Phase 0 |
**Completeness:** PASS — All six stories with dependencies satisfied by Phase 2 or earlier and belonging to the enhanced event-page domain are included in Phase 3. No eligible story appears to be missing.
**Parallelizability:** PASS (with one documented intra-phase sequential pair)
- US-6, US-8, US-11: mutually independent; no intra-phase dependencies. ✓
- US-10a: independent of US-6, US-8, US-11 within the phase. ✓
- US-10b → US-10a: **intra-phase dependency**. US-10b must follow US-10a. This forces sequential implementation of the pair within Phase 3. This is explicitly acknowledged in the spec: "US-10b: Must follow US-10a" and "US-10a → US-10b (sequential pair)" in the recommended order note. Not a violation — the constraint is documented and correct.
- US-9: independent of US-6, US-8, US-10a, US-10b, US-11 within Phase 3 (its cross-phase deps US-2 and US-5 are already satisfied). ✓
**Diagram consistency (minor observation, no ordering problem):** The Mermaid diagram shows only `US-2 → US-10a` for US-10a's dependencies. US-10a's declared dependencies include `US-1` in addition to US-2, but US-1 is a transitive prerequisite of US-2, so US-1 is reachable in the diagram via `US-1 → US-2 → US-10a`. This is a diagram simplification, not a missing dependency — the implementation order is unambiguous.
**Verdict:** Phase 3 is correctly ordered. No dependency violations found. The US-10a → US-10b sequential constraint is known and documented.
---
## Iteration 5 — 2026-03-03
**Task:** Verify Phase 4 (Visual Customization)
**Verified phases:** Phase 0, Phase 1, Phase 2, Phase 3, Phase 4
**Method:** Checked each story's position in Phase 4 against its declared `Dependencies:` field in spec/userstories.md. Phases 03 are treated as already verified.
### Findings
**Dependency satisfaction:** PASS
| Story | Declared Dependencies | All deps in earlier phases? |
|-------|-----------------------|-----------------------------|
| US-15 | US-1, US-2, T-4 | ✓ US-1 is Phase 1; US-2 is Phase 1; T-4 is Phase 0 |
| US-16 | US-1, US-2, T-4 | ✓ US-1 is Phase 1; US-2 is Phase 1; T-4 is Phase 0 |
**Completeness:** PASS — both visual customization stories (US-15, US-16) are accounted for in Phase 4. No other story in the spec belongs to this domain without being included here.
**Parallelizability:** PASS — US-15 and US-16 share no declared dependency on each other. They are structurally independent and can be worked on in parallel. The recommended order (US-15 before US-16) is an efficiency hint (simpler story, no external API dependency, and the interaction with dark/light mode is easier to resolve first), not a structural constraint.
**Undeclared dependency observation (no violation):** US-15 and US-16 both state in their ACs "During event creation or editing (US-5)" — implying functional integration with the edit form from US-5. US-5 is not listed as a declared dependency for either story. This omission is defensible because:
1. The core functionality (selecting a theme or image during event creation) is implementable using the creation form from US-1 alone.
2. Phase 4 comes after Phase 2 where US-5 is implemented, so US-5 will be available in practice when US-15 and US-16 are worked on.
No ordering violation.
**Grouping note (no violation):** US-15 and US-16 have declared dependencies satisfied after Phase 1 (same as several Phase 3 stories). Their placement in Phase 4 rather than Phase 3 is a deliberate functional grouping decision — visual customization is a separate concern from enhanced event page features. This is consistent with the documented grouping rationale in the spec. No scheduling error.
**Verdict:** Phase 4 is correctly ordered. No dependency violations found.
---
## Iteration 6 — 2026-03-03
**Task:** Verify Phase 5 (App Shell & PWA)
**Verified phases:** Phase 0, Phase 1, Phase 2, Phase 3, Phase 4, Phase 5
**Method:** Checked each story's position in Phase 5 against its declared `Dependencies:` field in spec/userstories.md. Phases 04 are treated as already verified.
### Findings
**Dependency satisfaction:** PASS
| Story | Declared Dependencies | All deps in earlier phases? |
|-------|-----------------------|-----------------------------|
| US-7 | None | ✓ (no deps) |
| US-14 | T-4 | ✓ T-4 is Phase 0 |
| US-17 | None | ✓ (no deps) |
**Completeness:** PASS — all three App Shell & PWA stories (US-7, US-14, US-17) are accounted for in Phase 5. No other story in the spec belongs to this domain without being included here.
**Parallelizability:** PASS — US-7, US-14, and US-17 share no declared dependencies on each other. They are structurally independent and can be worked on in parallel. The recommended within-phase order (US-17 → US-7 → US-14) is a practical sequencing hint, not a structural constraint.
**Undeclared dependency observations (no violations):**
- US-7's ACs reference US-1, US-3, and US-6 as sources of localStorage data (organizer tokens, RSVP records, bookmarks). None of these are declared dependencies. This is intentional and correct: US-7 can be implemented and passes acceptance in its empty state without those stories. The references are "practically useful after" notes, not structural prerequisites.
- US-7 AC 7 references US-12 and US-19 ("event no longer exists" handling) — not declared dependencies. US-7 can implement the error path even before US-12/US-19 are available; the condition simply won't be triggerable in testing until those stories are done. No violation.
- US-14 AC 7 references US-7 (manifest start URL points to `/`, which serves the local event overview). US-7 is not declared as a dependency of US-14. Defensible: the start URL can be set to `/` regardless of whether US-7 is implemented. No violation.
- US-17's notes state it "requires the frontend scaffold from T-4 (which includes T-1) to be practically implementable" but correctly omits T-4 from the declared dependencies (since US-17 has no structural gating on T-4 — it is a styling/CSS concern and the declared deps list only hard structural prerequisites). Consistent with how US-7's notes treat T-4 similarly.
**Phase 5 timing note (no violation):** The spec notes that US-17 "can be started early once the frontend scaffold exists — consider implementing alongside Phase 2 or 3." This is sound advice given that US-15 (Phase 4) must consider the interaction between event color themes and dark/light mode. However, the placement of US-17 in Phase 5 is not a scheduling error — it is a logical grouping. The practical recommendation to implement it earlier is correctly surfaced as a note, not enforced via a dependency constraint. No violation.
**Verdict:** Phase 5 is correctly ordered. No dependency violations found.
---
## Iteration 2 — 2026-03-03
**Task:** Verify Phase 1 (Core Event Flow — Vertical Slice)
**Verified phases:** Phase 0, Phase 1
**Method:** Checked each story's position in Phase 1 against its declared `Dependencies:` field in spec/userstories.md. Phase 0 is treated as already verified.
### Findings
**Dependency satisfaction:** PASS
| Story | Declared Dependencies | Scheduled at | All deps in earlier slots? |
|-------|-----------------------|--------------|---------------------------|
| US-1 | T-4 | Phase 1 Order 1 | ✓ T-4 is Phase 0 Order 3 |
| US-2 | US-1, T-4 | Phase 1 Order 2 | ✓ US-1 is Phase 1 Order 1; T-4 is Phase 0 |
| US-3 | US-2, T-4 | Phase 1 Order 3 | ✓ US-2 is Phase 1 Order 2; T-4 is Phase 0 |
**Completeness:** PASS — US-1, US-2, US-3 are the only stories with all dependencies in Phase 0 or earlier. No eligible story is missing from Phase 1.
**Parallelizability:** PASS — The chain US-1 → US-2 → US-3 is strictly sequential; US-2 depends on US-1 and US-3 depends on US-2, so no parallelization is possible. The phase note "These three stories are strictly sequential" is accurate.
**Verdict:** Phase 1 is correctly ordered. No dependency violations found.

View File

@@ -0,0 +1,9 @@
# Questions
## Open
(No open questions.)
## Resolved
(No resolved questions.)

View File

@@ -0,0 +1,8 @@
=== Run started: 2026-03-03T12:41:18 | model=sonnet | max=10 ===
[12:42:21] Iteration 1 done
[12:43:23] Iteration 2 done
[12:45:10] Iteration 3 done
[12:46:24] Iteration 4 done
[12:47:44] Iteration 5 done
[12:49:13] COMPLETE after 6 iteration(s)

62
CLAUDE.md Normal file
View File

@@ -0,0 +1,62 @@
# fete
A privacy-focused, self-hostable PWA for event announcements and RSVPs. Alternative to Facebook Events or Telegram groups — reduced to the essentials.
## Project Statutes
These are the non-negotiable principles of this project. Every decision — architectural, technical, or design-related — must be consistent with them.
### Governance
- The agent works autonomously on implementation tasks.
- When facing architectural decisions, fundamental design questions, tech stack choices, or dependency selections: advise, propose options, and ask for approval before proceeding.
- Actively challenge decisions — including the developer's — when there are good reasons to. Don't be a yes-machine.
- When encountering problems, attempt to solve them independently first. Only escalate when stuck.
### Methodology
- Follow Research → Spec → Test → Implement → Review. No shortcuts.
- Never write implementation code without a specification.
- Always write tests before implementation (TDD). Red → Green → Refactor.
- Refactoring is permitted freely as long as it does not alter the fundamental architecture.
- No vibe coding. Every line of code must be intentional and traceable to a requirement.
### Privacy
- Privacy is a design constraint, not a feature. It shapes every decision from the start.
- No analytics, no telemetry — not even self-hosted.
- Never log PII or IP addresses on the server.
- For every feature, critically evaluate what data is necessary. Only store what is absolutely required for functionality.
- Never include external dependencies that phone home: no CDNs, no Google Fonts, no tracking-capable libraries.
### Quality
- KISS and grugbrain. Engineer it properly, but don't over-engineer.
- No workarounds. Always fix the root cause, even if it takes longer.
- Address technical debt immediately. Don't let it accumulate.
- Accessibility is a baseline requirement, not an afterthought.
### Dependencies
- Every dependency is a deliberate, justified decision.
- A dependency must provide substantial value and a significant portion of its features must actually be used.
- Dependencies must be actively maintained and open source. Copyleft is fine — the project is GPL-licensed.
- Never introduce a dependency that phones home or compromises user privacy.
### Language
- Conversation and brainstorming: German.
- Code, comments, commits, documentation: English — no exceptions.
### Deployment
- The project provides a Dockerfile. How and where it is deployed is the hoster's responsibility.
- A docker-compose example in the README is sufficient.
- Documentation lives in the README. No wiki, no elaborate docs site.
### Ralph Loops
- Autonomous work is done via Ralph Loops. See [.claude/rules/ralph-loops.md](.claude/rules/ralph-loops.md) for documentation.
- The loop runner is `ralph.sh`. Each run lives in its own directory under `.ralph/`.
- Run directories contain: `instructions.md` (prompt), `chief-wiggum.md` (directives), `answers.md` (human answers), `questions.md` (Ralph's questions), `progress.txt` (iteration log), `meta.md` (metadata), `run.log` (execution log).
- Project specifications (user stories, setup tasks, personas, etc.) live in `spec/`.

80
Ideen.md Normal file
View File

@@ -0,0 +1,80 @@
# fete
## Grundsätze
* Soll als PWA im Browser laufen
* Damit es sich wie eine normale app anfühlt
* Soll ein kleiner Helfer sein, den man schnell mal nutzen kann
* Ich will es mir selbst hosten können
* Das schließt eigentlich schon AI features aus und das ist okay
* Privacy als first class citizen
* Schon das Produktdesign muss mit privacy im sinn entworfen werden
* Keine Registrierung, kein login notwendig (nur z.B. einen code, um den "raum" zu finden oder so)
* Alternativ könnten etwaige anfallende Daten auch im local storage gespeichert werden
## Die Idee
Eine alternative zu Facebook Event Gruppen oder Telegram Gruppen, in denen eine Veranstaltung bekanntgegeben wird und Teilnahmen bestätigt werden
### Zielbild
Person erstellt via App eine Veranstaltung und schickt seine Freunden irgendwie via Link eine Einladung. Freunde können zurückmelden, ob sie kommen oder nicht.
## Gedankensammlung
* So ne Art Landingpage zu jedem Event
* Ein Link pro Event, den man z.B. in ne WhatsApp-Gruppe werfen kann
* Was, wie, wann, wo?
* Irgendwie auch Designbar, sofern man das will
* RSVP: "Ich komme" (mit Name) / "Ich komme nicht" (optional mit Name)
* Wird serverseitig gespeichert + im LocalStorage gemerkt
* Duplikatschutz: Kein perfekter Schutz ohne Accounts, aber gegen versehentliche Doppeleinträge reicht Gerätebindung via LocalStorage
* Gegen malicious actors (Fake-Namen spammen etc.) kann man ohne Accounts wenig machen — akzeptables Risiko (vgl. spliit)
* "Veranstaltung merken/folgen": Rein lokal, kein Serverkontakt, kein Name nötig
* Löst das Multi-Geräte-Problem: Am Handy zugesagt, am Laptop einfach "Folgen" drücken
* Auch nützlich für Unentschlossene, die sich das Event erstmal merken wollen
* View für den Veranstalter:
* Updaten der Veranstaltung
* Einsicht angemeldete Gäste, kann bei Bedarf Einträge entfernen
* Featureideen:
* Kalender-Integration: .ics-Download + optional webcal:// für Live-Updates bei Änderungen
* Änderungen zum ursprünglichen Inhalt (z.b. geändertes datum/ort) werden iwi hervorgehoben
* Veranstalter kann Updatenachrichten im Event posten, pro Device wird via LocalStorage gemerkt was man schon gesehen hat (Badge/Hervorhebung für neue Updates)
* QR Code generieren (z.B. für Plakate/Flyer)
* Ablaufdatum als Pflichtfeld, nach dem alle gespeicherten Daten gelöscht werden
* Übersichtsliste im LocalStorage: Alle Events die man zugesagt oder gemerkt hat (vgl. spliit)
* Sicherheit/Missbrauchsschutz:
* Nicht-erratbare Event-Tokens (z.B. UUIDs)
* Event-Erstellung ist offen, kein Login/Passwort/Invite-Code nötig
* Max aktive Events als serverseitige Konfiguration (env variable)
* Honeypot-Felder in Formularen (verstecktes Feld das nur Bots ausfüllen → Anfrage ignorieren)
* Abgrenzungskriterien
* KEIN Chat
* KEIN Discovery Feature über die App: Ohne Zugangslink geht nichts
* KEINE Planung des Events! Also kein "wer macht/bringt was?", "was machen wir überhaupt?"
## Getroffene Designentscheidungen
Die folgenden Punkte wurden in Diskussionen bereits geklärt und sind verbindlich.
* RSVP-System:
* Ein Link pro Event (NICHT individuelle Einladungslinks pro Person — zu umständlich für den Veranstalter)
* Gäste geben beim RSVP einen Namen an, das reicht
* Duplikate durch versehentliche Mehrfachanmeldung: LocalStorage-Gerätebindung reicht als Schutz
* Bewusste Doppelanmeldung/Spam: Akzeptables Risiko, Veranstalter kann Einträge manuell löschen
* Geräte-Sync ohne Account ist nicht sauber lösbar und das ist okay
* Missbrauchsschutz:
* Rate Limiting: Bewusst rausgelassen — zu viel Infra-Overhead für den Scope
* Captcha: Bewusst rausgelassen — entweder Privacy-Problem (Google) oder hässlich
* Admin-Passwort/Invite-Code für Event-Erstellung: Bewusst rausgelassen — die App soll organisch weitergegeben werden können
* Erfahrungswert: Spliit-Instanz läuft auch komplett offen ohne nennenswerte Probleme
* Stattdessen pragmatische Maßnahmen: Nicht-erratbare Tokens, Ablaufdatum als Pflichtfeld, Max Events per Konfiguration, Honeypot-Felder
* Zielgruppe:
* Primär Freundeskreise, nicht die breite Öffentlichkeit
* Trotzdem: Die App hängt im Internet, also muss man grundlegende Absicherung haben
* Architektur (bereits entschieden):
* SPA + RESTful API Backend, kein SSR
* Datenbank: PostgreSQL, wird separat gehostet (nicht im App-Container — der Hoster betreibt seinen eigenen Postgres)
* Organizer-Authentifizierung: Zwei separate UUIDs pro Event — ein öffentliches Event-Token (in der URL, für Gäste) und ein geheimes Organizer-Token (in localStorage, für Verwaltung). Interne DB-ID ist ein Implementierungsdetail.
* App wird als einzelner Docker-Container ausgeliefert, verbindet sich per Konfiguration (env variable) mit der externen Postgres-Instanz
* Techstack:
* Backend: Java (neuste LTS Version), Spring Boot, Maven, Hexagonal/Onion Architecture
* Frontend: Svelte (mit Vite als Bundler)
* Architekturentscheidungen die NOCH NICHT getroffen wurden (hier darf nichts eigenmächtig entschieden werden!):
* (derzeit keine offenen Architekturentscheidungen)

674
LICENSE Normal file
View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

143
README.md Normal file
View File

@@ -0,0 +1,143 @@
# fete
A privacy-focused, self-hostable web app for event announcements and RSVPs. An alternative to Facebook Events or Telegram groups — reduced to the essentials.
## What it does
- **Create events** with title, description, date, time, location, and an expiry date
- **Share a single link** — guests open it, see all details, and RSVP
- **No accounts, no login** — the organizer gets a secret token stored in the browser; guests just enter a name
- **Automatic cleanup** — all event data is permanently deleted after the expiry date
- **Works offline-first** — installable as a PWA, local event list stored entirely in the browser
### For organizers
- Edit event details, post update messages, manage the guest list
- Cancel or delete events at any time
- Generate a QR code for posters and flyers
- Optionally pick a color theme or header image (via self-hosted Unsplash proxy)
### For guests
- RSVP with just a name — no personal data required
- Bookmark events locally across devices
- Download `.ics` or subscribe via `webcal://` for calendar integration
- See highlighted changes and new update indicators on revisit
### Privacy by design
- No analytics, no telemetry, no external requests from the client
- No CDNs, no Google Fonts, no tracking-capable dependencies
- Server never logs PII or IP addresses
- Data retention is bounded by a mandatory expiry date
## Tech stack
| Layer | Technology |
|--------------|------------------------------------------|
| Backend | Java (latest LTS), Spring Boot, Maven |
| Frontend | Svelte, Vite |
| Database | PostgreSQL (external, not bundled) |
| Architecture | SPA + RESTful API, hexagonal/onion backend |
| Deployment | Single Docker container |
## Self-hosting
### Prerequisites
- Docker and Docker Compose
- A PostgreSQL instance (self-hosted or managed)
### Quick start
```yaml
# docker-compose.yml
services:
fete:
image: fete:latest
ports:
- "8080:8080"
environment:
DATABASE_URL: "jdbc:postgresql://db:5432/fete"
# Optional: limit simultaneous active events
# MAX_ACTIVE_EVENTS: 100
# Optional: enable Unsplash image search for event headers
# UNSPLASH_API_KEY: your-api-key
depends_on:
- db
db:
image: postgres:17
environment:
POSTGRES_DB: fete
POSTGRES_USER: fete
POSTGRES_PASSWORD: changeme
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
```
```bash
docker compose up -d
```
The app runs at `http://localhost:8080`. Database migrations run automatically on startup.
### Configuration
All configuration is done via environment variables:
| Variable | Required | Description |
|----------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------|
| `DATABASE_URL` | Yes | JDBC connection string for PostgreSQL |
| `MAX_ACTIVE_EVENTS` | No | Maximum number of non-expired events. Unset = unlimited |
| `UNSPLASH_API_KEY` | No | Enables header image search via Unsplash. Images are fetched server-side and stored locally — guests never contact Unsplash |
### Image storage
If you use the Unsplash header image feature, mount a persistent volume for stored images so they survive container restarts. The exact path will be documented once the feature is implemented.
## Development
### Prerequisites
- Java (latest LTS) + Maven
- Node.js + npm
- PostgreSQL (or Docker for Testcontainers)
### Project structure
```
fete/
├── backend/ # Spring Boot application (Maven)
├── frontend/ # Svelte SPA (Vite)
├── spec/ # User stories, personas, implementation phases
├── Dockerfile # Multi-stage build for production
└── CLAUDE.md # Project statutes and AI agent instructions
```
### Running tests
```bash
# Backend (uses Testcontainers — needs Docker running)
cd backend && mvn test
# Frontend
cd frontend && npm test
```
### Building
```bash
# Backend
cd backend && mvn package
# Frontend
cd frontend && npm run build
```
## License
GPL — see [LICENSE](LICENSE) for details.

164
ralph.sh Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: ralph.sh <run-directory> [options]
Arguments:
run-directory Path to the Ralph run directory (must contain instructions.md)
Options:
-n, --max-iterations N Maximum iterations (default: 20)
-m, --model MODEL Claude model to use (default: opus)
-t, --tools TOOLS Allowed tools, quoted (default: "Read Edit Write")
-h, --help Show this help message
Examples:
./ralph.sh .ralph/my-run
./ralph.sh .ralph/my-run -n 10 -m sonnet
./ralph.sh .ralph/my-run -n 30 -t "Read Edit Write Bash Glob Grep"
EOF
exit 0
}
# Defaults
MAX_ITERATIONS=20
MODEL="opus"
TOOLS="Read Edit Write"
RUN_DIR=""
# Parse args
while [[ $# -gt 0 ]]; do
case $1 in
-n|--max-iterations) MAX_ITERATIONS="$2"; shift 2 ;;
-m|--model) MODEL="$2"; shift 2 ;;
-t|--tools) TOOLS="$2"; shift 2 ;;
-h|--help) usage ;;
-*) echo "Error: Unknown option: $1" >&2; exit 1 ;;
*) RUN_DIR="$1"; shift ;;
esac
done
if [[ -z "$RUN_DIR" ]]; then
echo "Error: No run directory specified." >&2
echo "Run './ralph.sh --help' for usage." >&2
exit 1
fi
if [[ ! -d "$RUN_DIR" ]]; then
echo "Error: Run directory '$RUN_DIR' does not exist." >&2
exit 1
fi
if [[ ! -f "$RUN_DIR/instructions.md" ]]; then
echo "Error: '$RUN_DIR/instructions.md' not found." >&2
echo "Create an instructions.md in the run directory before starting." >&2
exit 1
fi
# Auto-create template files if they don't exist
RUN_NAME=$(basename "$RUN_DIR")
TODAY=$(date +%Y-%m-%d)
if [[ ! -f "$RUN_DIR/meta.md" ]]; then
cat > "$RUN_DIR/meta.md" <<EOF
# Run: $RUN_NAME
- **Created**: $TODAY
- **Description**: (add description here)
- **Model**: $MODEL
- **Max iterations**: $MAX_ITERATIONS
EOF
echo "Created $RUN_DIR/meta.md"
fi
if [[ ! -f "$RUN_DIR/chief-wiggum.md" ]]; then
cat > "$RUN_DIR/chief-wiggum.md" <<'EOF'
# Chief Wiggum's Notes
<!-- This file is written by the Chief Wiggum session. Ralph reads it but never modifies it. -->
## Action Required
(No action items.)
## Observations
(No observations.)
EOF
echo "Created $RUN_DIR/chief-wiggum.md"
fi
if [[ ! -f "$RUN_DIR/answers.md" ]]; then
cat > "$RUN_DIR/answers.md" <<'EOF'
# Answers
<!-- Human answers to open questions. Ralph processes these one per iteration. -->
EOF
echo "Created $RUN_DIR/answers.md"
fi
if [[ ! -f "$RUN_DIR/questions.md" ]]; then
cat > "$RUN_DIR/questions.md" <<'EOF'
# Questions
## Open
(No open questions.)
## Resolved
(No resolved questions.)
EOF
echo "Created $RUN_DIR/questions.md"
fi
if [[ ! -f "$RUN_DIR/progress.txt" ]]; then
cat > "$RUN_DIR/progress.txt" <<'EOF'
# Ralph Loop Progress Log
# Each iteration appends its findings and decisions here.
EOF
echo "Created $RUN_DIR/progress.txt"
fi
# Prepare prompt with {{RUN_DIR}} substitution
PROMPT=$(sed "s|{{RUN_DIR}}|$RUN_DIR|g" "$RUN_DIR/instructions.md")
COMPLETION_SIGNAL="<promise>COMPLETE</promise>"
TIMESTAMP=$(date +%Y-%m-%dT%H:%M:%S)
# Append to run.log
echo "" >> "$RUN_DIR/run.log"
echo "=== Run started: $TIMESTAMP | model=$MODEL | max=$MAX_ITERATIONS ===" >> "$RUN_DIR/run.log"
echo "=== Ralph Loop ==="
echo "Run dir: $RUN_DIR"
echo "Model: $MODEL"
echo "Max iter: $MAX_ITERATIONS"
echo "Tools: $TOOLS"
echo ""
for ((i = 1; i <= MAX_ITERATIONS; i++)); do
echo "--- Iteration $i/$MAX_ITERATIONS ---"
OUTPUT=$(echo "$PROMPT" | claude --print --model "$MODEL" --allowedTools "$TOOLS" 2>&1)
ITER_TIME=$(date +%H:%M:%S)
if [[ "$OUTPUT" == *"$COMPLETION_SIGNAL"* ]]; then
echo "[$ITER_TIME] COMPLETE after $i iteration(s)" >> "$RUN_DIR/run.log"
echo "Loop complete after $i iteration(s)."
exit 0
fi
echo "[$ITER_TIME] Iteration $i done" >> "$RUN_DIR/run.log"
echo "Iteration $i done. Continuing..."
echo ""
sleep 2
done
ITER_TIME=$(date +%H:%M:%S)
echo "[$ITER_TIME] WARNING: Max iterations ($MAX_ITERATIONS) reached without completion" >> "$RUN_DIR/run.log"
echo "WARNING: Max iterations ($MAX_ITERATIONS) reached without completion."
exit 1

185
review-findings.md Normal file
View File

@@ -0,0 +1,185 @@
# Review Findings — Technische Vorbereitung
Reviewer-Perspektive: Greenfield-Projekt, keine einzige Zeile Code vorhanden. Prüfung auf Umsetzbarkeit, Vorbereitung, Blockaden, Größe und Aufbaureihenfolge.
## Gesamtbewertung
Die Vorarbeit ist bemerkenswert gründlich. 19 User Stories, 3 Setup Tasks, definierte Personas, ein konsistentes Token-Modell, durchgängige Privacy-Berücksichtigung, und ein sauberer iterativer Refinement-Prozess über 29 Iterationen. Die Story-Qualität ist hoch — klare ACs, testbar, konsistente Terminologie, Edge Cases abgedeckt.
Trotzdem gibt es konkrete Probleme, die vor der Umsetzung gelöst werden sollten.
---
## Finding 1: Fehlende Dependencies auf Setup-Tasks (kritisch)
**Keine einzige User Story hat eine Dependency auf T-1 oder T-2.** Das ist für ein Greenfield-Projekt ein echtes Problem.
US-1 sagt `Dependencies: None` — aber man kann keinen Event-Endpoint schreiben, wenn es kein Spring Boot Projekt, keine Datenbank-Verbindung und kein Frontend-Routing gibt. Faktisch hängen **alle** Backend-Stories von T-1 und T-2 ab, und **alle** Frontend-Stories mindestens von T-1.
**Empfehlung:** Entweder explizite Dependencies auf T-1/T-2 in die Stories aufnehmen, oder einen deutlichen Hinweis in der Doku, dass T-1 und T-2 die implizite Voraussetzung für alles sind. Sonst ist die Dependency-Darstellung irreführend.
---
## Finding 2: Fehlende Setup-Tasks für ein Greenfield-Projekt
T-1 scaffoldet die Verzeichnisstruktur, T-2 macht Dockerfile + Config. Aber es fehlt:
- **DB-Migration-Setup** (Flyway oder Liquibase) — wo/wann werden Tabellen angelegt? T-2 sagt nur nebulös „migrations run on startup or are documented". Für Hexagonal Architecture mit Spring Boot ist das ein echter Architektur-Schritt.
- **SPA-Routing-Grundgerüst** — Svelte braucht ein Router-Setup (z.B. svelte-routing oder SvelteKit Routing). Ohne das kann keine einzige Page-Story umgesetzt werden.
- **API-Client-Layer im Frontend** — Wie ruft das Frontend die REST API? Fetch-Wrapper? Generierter Client? Das ist bei Hexagonal Architecture eine bewusste Entscheidung.
- **Test-Infrastruktur** — CLAUDE.md verlangt TDD. Aber weder T-1 noch eine andere Task definiert, dass z.B. Testcontainers für Integrationstests, Vitest für Frontend-Tests, oder eine Test-DB-Strategie aufgesetzt wird. T-1 sagt nur „both projects build successfully" — das reicht nicht als Test-Fundament.
**Empfehlung:** Mindestens ein T-4 (oder T-1 erweitern) das explizit abdeckt:
- DB-Migration-Framework aufsetzen + erste leere Migration
- SPA-Router konfigurieren
- Test-Infrastruktur (Unit + Integration) aufsetzen und mit einer Smoke-Test-Suite verifizieren
---
## Finding 3: US-1 ist für ein Greenfield-Projekt die „Monster-Story"
US-1 hat 8 ACs, aber implizit beinhaltet sie den gesamten Erstaufbau des Stacks:
| Schicht | Was US-1 implizit verlangt |
|---------|---------------------------|
| DB | Event-Tabelle, Migration, Flyway-Setup |
| Backend | Entity, Repository, Service, REST Controller, UUID-Generation, Honeypot-Validation, JSON-Serialisierung |
| Frontend | Formular, Validierung, API-Call, localStorage-Management, Routing zur Erstellungsseite, Redirect zur Event-Page |
| Integration | Frontend↔Backend Verbindung, CORS-Konfiguration |
Jedes nachfolgende Ticket (US-2, US-3, ...) profitiert davon, dass US-1 diesen ganzen Unterbau bereits etabliert hat. Aber US-1 selbst trägt die gesamte Last.
**Empfehlung:** Entweder:
- (a) Das bewusst akzeptieren und US-1 als „Durchstich-Story" betrachten, die naturgemäß größer ist — aber das explizit dokumentieren.
- (b) US-1 splitten in US-1a (Backend: Event erstellen + API Endpoint + DB) und US-1b (Frontend: Formular + localStorage + Redirect). Das ermöglicht unabhängiges Arbeiten und kleinere Reviews.
---
## Finding 4: Forward-References in ACs — unklar, was wann implementiert wird
Mehrere Stories referenzieren Features, die erst durch spätere Stories existieren:
| Story | AC referenziert | Problem |
|-------|----------------|---------|
| US-2 AC 5 | „If the event has been cancelled (US-18)..." | US-18 kommt erst viel später |
| US-2 AC 6 | „deleted after expiry per US-12" / US-19 | US-12/US-19 kommen später |
| US-3 AC 10 | „not possible if cancelled (US-18)" | US-18 kommt später |
| US-8 AC 9 | „STATUS:CANCELLED" (US-18) | US-18 kommt später |
| US-12 AC 2 | „stored header images (US-16)" | US-16 kommt später |
Technisch ist das kein Dependency-Problem — man kann diese ACs weglassen und nachrüsten. Aber es ist nirgends dokumentiert, ob:
- diese ACs bei der Erstimplementierung schon umgesetzt werden sollen (dann SIND es Dependencies), oder
- sie erst nachgerüstet werden, wenn die referenzierte Story fertig ist (dann sollten sie als solche markiert sein).
**Empfehlung:** Jede Forward-Reference als `[deferred until US-X]` markieren oder die ACs in die referenzierte Story verschieben. Zum Beispiel: Der cancelled-state-Display gehört logisch in US-18, nicht in US-2 — US-18 sollte dann ein AC haben: „US-2 zeigt cancelled state an."
---
## Finding 5: Split-Kandidaten
### US-10 (Update Messages) — 11 ACs, zwei verschiedene Concerns
US-10 enthält zwei klar trennbare Funktionen:
1. **Nachrichten posten, anzeigen, löschen** (AC 1-5, 10-11) — Backend + Frontend
2. **„New update" Badge/Indicator** (AC 5-9) — rein localStorage-basiert
**Empfehlung:** Splitten in:
- US-10a: Organizer kann Update-Nachrichten posten und löschen; Gäste sehen sie chronologisch
- US-10b: „Neue Updates"-Badge basierend auf localStorage-Tracking
### US-16 (Unsplash Header Image) — 11 ACs, externe Dependency
US-16 ist komplex (Server-Proxy, Unsplash API, lokale Bildspeicherung, Attribution, Graceful Degradation, Volume Mount). Aber es ist ein kohärentes Feature und lässt sich schwer sinnvoll vertikal splitten. Kein Split nötig, aber als eine der aufwändigeren Stories markieren.
### US-7 (Local Event Overview) — 11 ACs, drei Datenquellen + Entry-Removal
Grenzwertig. Die Entry-Removal-Logik (AC 9-10: drei verschiedene Verhaltensweisen für Organizer/Guest/Bookmark-only Einträge) ist ein eigener Mini-Workflow. Optional splitten, aber nicht zwingend.
---
## Finding 6: Q-5 (CI/CD Platform) ist noch offen
T-3 kann nicht umgesetzt werden. Das blockiert zwar nichts am Anfang, sollte aber geklärt werden, bevor die erste Story fertig ist — sonst hat man Code ohne Pipeline.
---
## Finding 7: Fehlende Implementierungsreihenfolge / Phasenplan
Dependencies existieren, aber ein expliziter Phasenplan fehlt. Für ein Greenfield-Projekt ist eine klare Reihenfolge essenziell, weil jede Phase auf der vorherigen aufbaut. Basierend auf dem Dependency-Graph:
```
Phase 0 — Infrastruktur
T-1 → T-2 (→ T-3 nach Q-5)
Phase 1 — Minimal Viable Flow (End-to-End Durchstich)
US-1 (Event erstellen)
US-2 (Event anzeigen)
US-3 (RSVP)
Phase 2 — Organizer-Fähigkeiten
US-4 (Gästeliste verwalten)
US-5 (Event bearbeiten)
Phase 3 — Client-seitige Features (parallelisierbar)
US-6 (Bookmark)
US-7 (Lokale Übersicht)
US-17 (Dark/Light Mode)
Phase 4 — Zusatzfeatures (parallelisierbar)
US-8 (Kalender .ics/webcal)
US-9 (Change Highlighting) — braucht US-5
US-10 (Update Messages)
US-11 (QR Code)
Phase 5 — Server-Infrastruktur
US-12 (Auto-Deletion)
US-13 (Max Events Limit)
Phase 6 — Design & Customization
US-14 (PWA)
US-15 (Color Themes)
US-16 (Unsplash Header Image)
Phase 7 — Event-Lifecycle (braucht soliden Grundflow)
US-18 (Event Cancellation)
US-19 (Event Deletion)
```
**Anmerkung zu Phase 7:** US-18 und US-19 haben zwar nur `Dependencies: US-1`, sind aber in der Praxis erst sinnvoll testbar, wenn US-2, US-3, US-4, US-5 existieren. Die reine Dependency-Analyse unterschätzt das.
---
## Finding 8: Kleinere Inkonsistenzen
### US-14 (PWA) sagt `Dependencies: None`
Ein Service Worker braucht Assets zum Cachen. US-14 ist technisch unabhängig, aber praktisch erst sinnvoll, wenn es Pages gibt (US-1, US-2, US-7). Sollte mindestens als Hinweis in den Notes stehen.
### US-7 sagt `Dependencies: None`
Rendert aus localStorage-Daten, die von US-1, US-3, US-6 geschrieben werden. Ohne diese Quellen hat US-7 nur den Empty State. Technisch korrekt als „None", aber irreführend — ein Hinweis wie „praktisch erst nach US-1/US-3/US-6 sinnvoll testbar" wäre hilfreich.
### US-15 und US-17 Wechselwirkung
Beide Notes erwähnen das Problem (Event-Themes vs. Dark/Light Mode), aber kein AC definiert konkretes Verhalten. Was passiert, wenn ein helles Event-Theme in Dark Mode angezeigt wird? Das ist kein Story-Problem, aber eine offene Design-Frage, die VOR der Implementierung geklärt sein sollte.
---
## Zusammenfassung der Handlungsempfehlungen
| Prio | Thema | Handlung |
|------|-------|----------|
| **Hoch** | Dependencies auf T-1/T-2 | Explizit machen oder dokumentieren |
| **Hoch** | Fehlende Setup-Tasks | T-4 für DB-Migrations, Router, Test-Infra |
| **Hoch** | Forward-References | Als `[deferred until US-X]` markieren |
| **Hoch** | Phasenplan | Explizite Reihenfolge dokumentieren |
| **Mittel** | US-1 Größe | Als Durchstich akzeptieren ODER splitten |
| **Mittel** | US-10 splitten | Post/Display vs. Badge trennen |
| **Mittel** | Q-5 klären | CI/CD Platform entscheiden |
| **Niedrig** | US-7/US-14 „None" Dependencies | Notes ergänzen |
| **Niedrig** | US-15/US-17 Wechselwirkung | Design-Entscheidung treffen |

View File

@@ -0,0 +1,156 @@
# Implementation Phases
A recommended implementation order based on the dependency graph across all stories and setup tasks.
## Phase 0: Project Infrastructure
All setup tasks must complete before any user story work begins. T-3 can run in parallel with Phase 1.
| Order | Task | Depends on | Notes |
|-------|------|------------|-------|
| 1 | T-1: Initialize monorepo structure | — | Scaffolds empty backend + frontend projects |
| 2 | T-2: Deployment setup (Dockerfile + config) | T-1 | Docker build, DB connection, health check |
| 3 | T-4: Development infrastructure | T-1, T-2 | Migrations, router, API client, test infra — gates all user stories |
| 3* | T-3: CI/CD pipeline | T-1, T-2 | Parallelizable with T-4. Uses Gitea Actions (per Q-5 resolution) |
## Phase 1: Core Event Flow (Vertical Slice)
The end-to-end journey from creating an event to viewing it to RSVPing. US-1 is the "Durchstich" that bootstraps the full stack (DB table, backend endpoint, frontend form, localStorage, routing). It will take significantly longer than subsequent stories.
| Order | Story | Depends on | Parallelizable |
|-------|-------|------------|----------------|
| 1 | US-1: Create an event | T-4 | — |
| 2 | US-2: View event landing page | US-1 | — |
| 3 | US-3: RSVP to an event | US-2 | — |
These three stories are strictly sequential. No parallelization within this phase.
## Phase 2: Organizer Management & Event Lifecycle
All stories in this phase depend on US-1 (and T-4 transitively). They can be implemented in parallel since they are independent of each other. Some are more useful after Phase 1 completes (e.g. US-4 needs RSVPs to manage), but they are structurally implementable after US-1.
| Story | Depends on | Notes |
|-------|------------|-------|
| US-4: Manage guest list | US-1 | Most useful after US-3 provides RSVPs to manage |
| US-5: Edit event details | US-1 | Required by US-9 in Phase 3 |
| US-18: Cancel an event | US-1 | Enables deferred ACs in US-2, US-3, US-8 |
| US-19: Delete an event | US-1 | Enables deferred AC in US-2 |
| US-12: Automatic data deletion after expiry | US-1 | Enables deferred AC in US-2; server-side scheduled job |
| US-13: Limit active events | US-1 | Server-side config; independent of all other stories |
**Recommended order within phase:** US-5, US-4, US-18, US-19, US-12, US-13 — starting with US-5 because US-9 (Phase 3) depends on it, and US-4 because it completes the organizer toolset around RSVPs.
## Phase 3: Enhanced Event Page Features
Features that enrich the event page for guests. Most depend on US-2 (event page exists). US-9 additionally requires US-5 (editing). US-10b requires US-10a.
| Story | Depends on | Parallelizable with |
|-------|------------|---------------------|
| US-6: Bookmark an event | US-2 | US-8, US-10a, US-11 |
| US-8: Add event to calendar (.ics / webcal) | US-2 | US-6, US-10a, US-11 |
| US-9: Highlight changed event details | US-2, US-5 | US-6, US-8, US-10a, US-11 (if US-5 is done) |
| US-10a: Post update messages | US-1, US-2 | US-6, US-8, US-11 |
| US-10b: New-update indicator | US-10a | Must follow US-10a |
| US-11: Generate QR code | US-2 | US-6, US-8, US-10a |
**Recommended order within phase:** US-6, US-8, US-11 (simple, independent), then US-10a → US-10b (sequential pair), then US-9 (requires US-5 from Phase 2).
## Phase 4: Visual Customization
Event-level theming and image selection. Both depend on US-1 and US-2. US-15 and US-16 are independent of each other but share the event creation/editing form surface area, so coordinating them is beneficial.
| Story | Depends on | Notes |
|-------|------------|-------|
| US-15: Choose event color theme | US-1, US-2 | Predefined theme picker in creation/edit forms |
| US-16: Select header image from Unsplash | US-1, US-2 | Optional feature gated by API key config |
**Recommended order:** US-15 first (simpler, no external dependency), then US-16. Consider the interaction between event themes and dark/light mode (US-17) — implement US-17 before or alongside US-15 if possible.
## Phase 5: App Shell & PWA
Client-side infrastructure and app-level UX features. These have no or minimal structural dependencies but are only meaningfully testable after earlier phases provide content and data.
| Story | Depends on | Practically useful after |
|-------|------------|------------------------|
| US-7: Local event overview | None (structural) | US-1, US-3, US-6 populate localStorage |
| US-14: Install as PWA | T-4 (structural) | US-2, US-7 provide pages to cache |
| US-17: Dark/light mode | None (structural) | T-4 provides frontend scaffold |
**Recommended order:** US-17 (can be started early once the frontend scaffold exists — consider implementing alongside Phase 2 or 3), then US-7 (after localStorage-populating stories are available), then US-14 (after the app has real pages and assets).
**Note on US-17 timing:** US-17 is listed in Phase 5 for logical grouping, but it can be implemented as early as Phase 2 since it only needs the frontend scaffold. Implementing it earlier is recommended because US-15 (Phase 4) must consider the interaction between event color themes and dark/light mode. Having dark/light mode in place before US-15 simplifies that work.
## Deferred Acceptance Criteria
Several stories contain ACs that reference features from later phases. These are marked `[deferred until US-X is implemented]` in the story text:
| Story | AC | Deferred until | Phase unlocked |
|-------|-----|---------------|----------------|
| US-2 AC 5 | Cancelled state display | US-18 | Phase 2 |
| US-2 AC 6 | Event not found (expiry deletion) | US-12 | Phase 2 |
| US-2 AC 6 | Event not found (organizer deletion) | US-19 | Phase 2 |
| US-3 AC 11 | RSVP blocked on cancelled event | US-18 | Phase 2 |
| US-8 AC 9 | STATUS:CANCELLED in .ics | US-18 | Phase 2 |
| US-12 AC 2 | Delete stored header images | US-16 | Phase 4 |
Once the referenced story is implemented, revisit the deferring story to activate the deferred AC.
## Dependency Graph
Render this diagram at [mermaid.live](https://mermaid.live) or view it directly in Gitea (which renders `mermaid` blocks natively).
```mermaid
graph TD
classDef infra fill:#4a90d9,stroke:#2c5f8a,color:#fff
classDef core fill:#e8a838,stroke:#b07c1e,color:#fff
classDef organizer fill:#50b86c,stroke:#2d8043,color:#fff
classDef enhanced fill:#9b59b6,stroke:#6c3483,color:#fff
classDef visual fill:#e74c3c,stroke:#a93226,color:#fff
classDef shell fill:#7f8c8d,stroke:#566566,color:#fff
%% Phase 0: Infrastructure
T1(["T-1: Monorepo"]):::infra --> T2(["T-2: Docker & DB"]):::infra
T2 --> T4(["T-4: Dev Infra"]):::infra
T2 --> T3(["T-3: CI/CD"]):::infra
%% Phase 1: Core Event Flow
T4 --> US1["US-1: Create Event"]:::core
US1 --> US2["US-2: View Event"]:::core
US2 --> US3["US-3: RSVP"]:::core
%% Phase 2: Organizer & Lifecycle (branch from US-1)
US1 --> US4["US-4: Guest List"]:::organizer
US1 --> US5["US-5: Edit Event"]:::organizer
US1 --> US18["US-18: Cancel"]:::organizer
US1 --> US19["US-19: Delete"]:::organizer
US1 --> US12["US-12: Auto-Cleanup"]:::organizer
US1 --> US13["US-13: Event Limit"]:::organizer
%% Phase 3: Enhanced Features (branch from US-2)
US2 --> US6["US-6: Bookmark"]:::enhanced
US2 --> US8["US-8: Calendar .ics"]:::enhanced
US2 --> US10a["US-10a: Messages"]:::enhanced
US2 --> US11["US-11: QR Code"]:::enhanced
US5 --> US9["US-9: Change Highlights"]:::enhanced
US2 --> US9
US10a --> US10b["US-10b: New-Update Badge"]:::enhanced
%% Phase 4: Visual Customization (branch from US-2)
US2 --> US15["US-15: Color Themes"]:::visual
US2 --> US16["US-16: Unsplash Images"]:::visual
%% Phase 5: App Shell & PWA
T4 --> US14["US-14: PWA"]:::shell
US7["US-7: Local Overview"]:::shell
US17["US-17: Dark/Light Mode"]:::shell
```
**Legend:**
- 🔵 Infrastructure (T-1 T-4)
- 🟠 Core Event Flow (US-1 US-3)
- 🟢 Organizer & Lifecycle (US-4, US-5, US-12, US-13, US-18, US-19)
- 🟣 Enhanced Features (US-6, US-8 US-11)
- 🔴 Visual Customization (US-15, US-16)
- ⚪ App Shell & PWA (US-7, US-14, US-17)
US-7 and US-17 appear as isolated nodes — they have no structural dependencies but are only practically useful after earlier phases provide content (see Phase 5 notes above).

95
spec/personas.md Normal file
View File

@@ -0,0 +1,95 @@
# Personas
<!-- Extracted from user stories. These are the roles referenced across all stories. -->
## Event Organizer
**Description:** A person who creates and manages an event using the app.
**Goals and motivations:**
- Create an event quickly without needing to register or log in
- Share the event with friends via a simple link
- Keep event details up to date
- See who is attending and manage the guest list
- Optionally personalize the event page's appearance
**Capabilities:**
- Create events (US-1)
- Edit event details (US-5)
- Cancel an event with an optional cancellation message (US-18)
- Delete an event immediately and permanently (US-19)
- View and manage the guest list — including removing entries (US-4)
- Post update messages to the event (US-10a)
- Choose a color theme for the event page (US-15)
- Select a header image from Unsplash (US-16)
- Generate and download a QR code for the event (US-11)
- View local event overview (US-7, shared with Guest — story uses "user" role)
- Switch between dark/light mode (US-17, shared with Guest — story uses "user" role)
**Limitations:**
- Organizer access is device-bound via the organizer token in localStorage (no cross-device organizer access without accounts)
- Cannot moderate beyond their own event (no global admin role)
- Losing the device or clearing localStorage means losing organizer access
**Appears in:** US-1, US-4, US-5, US-7, US-10a, US-11, US-15, US-16, US-17, US-18, US-19
---
## Guest
**Description:** A person who receives an event link and interacts with the event page. May be attending, declining, or just browsing.
**Goals and motivations:**
- View event details (what, when, where, who else is coming)
- RSVP to indicate attendance or non-attendance
- Keep track of events they're interested in across devices (via bookmarks)
- Add events to their personal calendar
- Stay informed about event updates or changes
**Capabilities:**
- View the event landing page (US-2)
- RSVP to an event (US-3)
- Bookmark an event locally (US-6)
- View the local event overview (US-7)
- Download .ics / subscribe via webcal (US-8)
- See highlighted changes to event details (US-9)
- See organizer update messages (US-10a)
- See new-update indicator for unread messages (US-10b)
- Download QR code for the event (US-11)
- Install the app as a PWA (US-14)
- Switch between dark/light mode (US-17)
**Limitations:**
- Cannot edit event details or manage the guest list
- Cannot post update messages
- RSVP duplicate protection is device-bound (localStorage), not identity-bound
- Local data (bookmarks, RSVP records, preferences) is device-bound and not synced across devices
**Note on sub-states:** A guest may be in different states relative to an event (RSVPed attending, RSVPed not attending, bookmarked only, or just viewing). These are contextual states within the same persona — the same person moves between them. The user stories handle these states through their acceptance criteria rather than defining separate roles.
**Appears in:** US-2, US-3, US-6, US-7, US-8, US-9, US-10a (as reader), US-10b, US-11, US-12, US-14, US-17
---
## Self-Hoster
**Description:** A technical person who deploys and operates an instance of the app on their own infrastructure.
**Goals and motivations:**
- Run a private, self-hosted event platform for their community
- Control resource usage and prevent abuse on their instance
- Configure the app via environment variables without modifying code
- Deploy easily using Docker
**Capabilities:**
- Configure maximum active events (US-13)
- Configure Unsplash API key for image search (US-16)
- Configure database connection and other runtime settings (T-2)
- Deploy using Docker with docker-compose (T-2)
**Limitations:**
- Not a user role in the app's UI — interacts only through deployment and configuration
- Cannot moderate individual events (that is the organizer's role)
- App design decisions are not influenced by the self-hoster at runtime
**Appears in:** US-13, US-16 (configuration aspect)

72
spec/setup-tasks.md Normal file
View File

@@ -0,0 +1,72 @@
# Setup Tasks
<!-- Technical setup tasks that are prerequisites for user story implementation. -->
<!-- These are not user stories — they describe infrastructure and project scaffolding work. -->
## Tasks
### T-1: Initialize monorepo structure
**Description:** Set up the repository structure with separate directories for backend and frontend, scaffolded with the chosen tech stack.
**Acceptance Criteria:**
- [ ] Single repository with `backend/` and `frontend/` directories
- [ ] Backend: Java (latest LTS), Spring Boot, Maven, hexagonal/onion architecture scaffold
- [ ] Frontend: Svelte with Vite as bundler
- [ ] Shared top-level files: README, Dockerfile, CLAUDE.md, LICENSE (GPL), .gitignore
- [ ] Both projects build successfully with no source code (empty scaffold)
- [ ] .gitignore covers build artifacts, IDE files, and dependency directories for both Java/Maven and Node/Svelte
**Dependencies:** None
---
### T-2: Deployment setup (Dockerfile + configuration)
**Description:** Create the Docker-based deployment infrastructure so the app can be built and run as a single container connecting to an external PostgreSQL database.
**Acceptance Criteria:**
- [ ] Single multi-stage Dockerfile at repo root that builds backend and frontend and produces one container
- [ ] App connects to external PostgreSQL via environment variable (e.g. `DATABASE_URL`)
- [ ] All runtime configuration via environment variables: database connection, optional Unsplash API key, optional max active events
- [ ] Health-check endpoint so Docker/orchestrators can verify the app is alive
- [ ] README documents setup with a docker-compose example (app + postgres)
- [ ] Container starts and responds to health checks with an empty database (migrations run on startup or are documented)
**Dependencies:** T-1
---
### T-3: CI/CD pipeline
**Description:** Set up a Gitea Actions CI/CD pipeline that runs on every push, ensuring code quality before deployment.
**Acceptance Criteria:**
- [ ] Gitea Actions workflow file in `.gitea/workflows/` runs on push: test, build, publish Docker image
- [ ] Backend tests run via Maven
- [ ] Frontend tests run via Vite/Vitest
- [ ] Docker image is published to the Gitea container registry on the same instance
- [ ] Pipeline fails visibly if any test fails or the build breaks
- [ ] Docker image is only published if all tests pass and the build succeeds
**Dependencies:** T-1, T-2
**Notes:** Per Q-5 resolution: the project uses Gitea as its hosting and CI/CD platform. The pipeline uses Gitea Actions (`.gitea/workflows/`) and publishes Docker images to the Gitea container registry. T-3 depends on T-1 (repository structure with both projects to test and build) and T-2 (Dockerfile used by the pipeline to build and publish the container image).
---
### T-4: Development infrastructure setup
**Description:** Set up the development foundation needed before the first user story can be implemented with TDD (as required by CLAUDE.md). This bridges the gap between empty project scaffolds (T-1) and actual feature development.
**Acceptance Criteria:**
- [ ] Database migration framework (Flyway or Liquibase) is configured in the backend with a first empty migration that runs successfully against a PostgreSQL instance
- [ ] SPA router is configured in the Svelte frontend so pages can be navigated by URL path
- [ ] API client layer exists in the frontend (a fetch wrapper or similar) for making requests to the backend REST API
- [ ] Backend test infrastructure is set up: JUnit 5 with Spring Boot Test, plus integration test support using Testcontainers (PostgreSQL) so tests can run against a real database without external setup
- [ ] Frontend test infrastructure is set up: Vitest configured and a sample test runs successfully
- [ ] Both test suites (backend and frontend) can be executed via their respective build tools (`mvn test` and `npm test` / `npx vitest`)
**Dependencies:** T-1, T-2
**Notes:** T-4 is the prerequisite for all user story implementation. Without migration tooling, router, API client, and test infrastructure, TDD (the mandated methodology per CLAUDE.md) cannot begin. All user stories that previously depended on T-1 and/or T-2 now depend on T-4 instead, since T-4 transitively includes both.

465
spec/userstories.md Normal file
View File

@@ -0,0 +1,465 @@
# User Stories
<!-- This file is managed by the Ralph Loop. Each iteration refines and adds user stories based on Ideen.md. -->
## Status
- Total stories: 20
- Complete: 0
- Remaining: 20
## Token Model
The following terms are used consistently across all stories:
- **Event token**: A public UUID embedded in the event URL. Used by guests to access the event page.
- **Organizer token**: A separate secret UUID stored in localStorage on the device where the event was created. Used to authenticate organizer actions.
- **Internal DB ID**: An implementation detail. Never exposed in stories, URLs, or to users.
## Stories
### US-1: Create an event
**As an** event organizer,
**I want to** create a new event with a title, description, date, time, location, and mandatory expiry date,
**so that** I can share it with others as a dedicated event page.
**Acceptance Criteria:**
- [ ] The organizer can fill in: title (required), description (optional), date and time (required), location (optional), expiry date (required)
- [ ] On submission, the server stores the event and returns both a unique, non-guessable event token (UUID) and a separate organizer token (UUID) in the creation response
- [ ] The organizer is redirected to the event page after creation
- [ ] The organizer token from the creation response is stored in localStorage to grant organizer access on this device
- [ ] The event token, title, and date are also stored in localStorage alongside the organizer token, so the local event overview (US-7) can display the event without additional server contact
- [ ] No account, login, or personal data is required to create an event
- [ ] The expiry date field is mandatory and cannot be left blank
- [ ] A honeypot field is present in the event creation form: hidden from real users; any submission with the field populated is silently discarded server-side
- [ ] The event is not discoverable except via its direct link
**Dependencies:** T-4
**Notes:** Non-guessable tokens (UUIDs) are specified in Ideen.md under security. Expiry date is mandatory per Ideen.md. No registration required per core principles. Per Q-4 resolution: organizer authentication uses the organizer token stored in localStorage on the device where the event was created. The organizer token is separate from the event token — since the event link is designed to be shared in group chats, using the same token for both public access and organizer auth would allow any guest to manage the event.
---
### US-2: View event landing page
**As a** guest,
**I want to** open a shared event link and see all event details,
**so that** I know what the event is, when and where it takes place, and who else is attending.
**Acceptance Criteria:**
- [ ] The event page displays: title, description (if provided), date and time, location (if provided)
- [ ] The page lists the names of all confirmed attendees (those who RSVPed "attending")
- [ ] The page shows a count of attendees
- [ ] If the event has expired (past its expiry date), the page renders a clear "this event has ended" state and no RSVP actions are shown
- [ ] If the event has been cancelled (US-18), the page displays a clear "cancelled" state with the cancellation message (if provided by the organizer), and no RSVP actions are shown [deferred until US-18 is implemented]
- [ ] If the event token does not match any event on the server (e.g. because it was deleted after expiry per US-12 [deferred until US-12 is implemented], or deleted by the organizer per US-19 [deferred until US-19 is implemented]), the page displays a clear "event not found" message — no partial data or error traces are shown
- [ ] The page is accessible without any login, account, or access code — only the event link is required
- [ ] No external resources (CDNs, fonts, tracking scripts) are loaded
**Dependencies:** US-1, T-4
**Notes:** Ideen.md describes "a kind of landing page for each event — what, when, where." The attendee list is needed here so guests can see who else confirmed. The expired-state requirement follows from the mandatory expiry date in US-1. The cancelled-state requirement follows from US-18: a cancelled event remains visible until its expiry date but clearly communicates its cancelled status. Per Q-3 resolution: the app is a SPA with a RESTful API backend; JavaScript-dependent rendering is acceptable. The "no external resources" criterion derives from the privacy statutes. The "event not found" criterion covers the edge case where a guest navigates to a previously valid event link after the server has deleted the event data (US-12).
---
### US-3: RSVP to an event
**As a** guest,
**I want to** indicate whether I will attend an event,
**so that** the organizer and other guests can see who is coming.
**Acceptance Criteria:**
- [ ] The guest can choose "I'm attending" or "I'm not attending"
- [ ] When selecting "I'm attending", a name is required
- [ ] When selecting "I'm not attending", providing a name is optional
- [ ] The RSVP is submitted to the server and persisted server-side
- [ ] The guest's RSVP choice and name are stored in localStorage to prevent accidental duplicate submissions from the same device
- [ ] The event token, title, and date are also stored in localStorage alongside the RSVP data, so the local event overview (US-7) can display the event and link to it without server contact
- [ ] If a prior RSVP exists in localStorage for this event, the form pre-fills with the previous choice and name
- [ ] Re-submitting from the same device updates the existing RSVP entry rather than creating a duplicate
- [ ] A honeypot field is present in the RSVP form: hidden from real users; any submission with the field populated is silently discarded server-side
- [ ] RSVP submission is not possible after the event's expiry date
- [ ] RSVP submission is not possible if the event has been cancelled (US-18) [deferred until US-18 is implemented]
- [ ] No account, login, or data beyond the optionally entered name is required
**Dependencies:** US-2, T-4
**Notes:** RSVP flow specified in Ideen.md: "Ich komme" (with name) / "Ich komme nicht" (optional with name). LocalStorage device binding is the explicit duplicate-prevention mechanism — not a hard guarantee, but sufficient against accidental duplicates. Ideen.md acknowledges that malicious spam without accounts is an acceptable risk. Honeypot fields are listed under Ideen.md security measures.
---
### US-4: Manage guest list as organizer
**As an** event organizer,
**I want to** view all RSVPs for my event and remove individual entries if needed,
**so that** I have an accurate overview of attendance and can moderate erroneous or spam entries.
**Acceptance Criteria:**
- [ ] An organizer view is accessible from the event page when a valid organizer token for that event is present in localStorage
- [ ] When no organizer token is present, no organizer-specific UI (link, button, or view) is shown to the visitor
- [ ] The organizer view lists all RSVPs, showing each entry's name and attending status
- [ ] The organizer can permanently delete any individual RSVP entry
- [ ] After deletion, the attendee list on the public event page updates immediately to reflect the removal
- [ ] The organizer view is not accessible via a guessable URL — it requires the organizer token stored in localStorage during event creation (US-1)
- [ ] No additional authentication step is required beyond the presence of the organizer token in localStorage
**Dependencies:** US-1, T-4
**Notes:** The organizer token is established in localStorage during event creation (US-1). Removal capability is specified in Ideen.md: "Einsicht angemeldete Gäste, kann bei Bedarf Einträge entfernen." The organizer view for *editing* event details is a separate story. Per Q-4 resolution: organizer access is confirmed as localStorage-based, using an organizer token separate from the event token. Organizer access is therefore device-bound.
---
### US-5: Edit event details as organizer
**As an** event organizer,
**I want to** update the details of an event I created,
**so that** guests always see accurate and up-to-date information if something changes.
**Acceptance Criteria:**
- [ ] The organizer can edit: title (required), description (optional), date and time (required), location (optional), expiry date (required)
- [ ] The expiry date can only be set to a date in the future — setting it to today or a past date is rejected with a clear validation message directing the organizer to use the delete feature (US-19) instead
- [ ] Editing is only accessible when a valid organizer token for the event is present in localStorage
- [ ] The edit form is pre-filled with the current event values
- [ ] Changes are persisted server-side upon submission
- [ ] After saving, the organizer is returned to the event page which reflects the updated details
- [ ] If the organizer token is absent or invalid, the edit UI is not shown and the server rejects the update request
- [ ] No account or additional authentication step is required beyond the organizer token
**Dependencies:** US-1, T-4
**Notes:** Ideen.md specifies "Updaten der Veranstaltung" as an organizer capability. Editing the expiry date is purely an edit operation. The expiry date must always be in the future — if the organizer wants an event gone immediately, they use the explicit delete feature (US-19) instead of manipulating the expiry date. Explicit event cancellation is a separate, dedicated action covered by US-18. Visual highlighting of changes on the public event page (Ideen.md: "Änderungen zum ursprünglichen Inhalt werden hervorgehoben") is a separate concern and is covered in US-9. Per Q-4 resolution: organizer authentication confirmed as localStorage-based organizer token.
---
### US-6: Bookmark an event
**As a** guest,
**I want to** bookmark an event on my current device without submitting an RSVP,
**so that** I can easily return to the event page later and stay aware of it without committing to attendance.
**Acceptance Criteria:**
- [ ] The event page shows a "Remember" / "Follow" action that requires no name or personal data
- [ ] Activating the action stores the event token, event title, and event date in localStorage — no server request is made
- [ ] The bookmark persists across browser sessions on the same device
- [ ] A second activation of the same action removes the bookmark ("unfollow"), again without any server contact
- [ ] The bookmark state is independent of the RSVP state: a guest who has already RSVPed on this device can still explicitly bookmark or un-bookmark the event
- [ ] If the event is expired, the bookmark action is still available (so the guest can still see it in their local overview)
- [ ] No personal data, IP address, or identifier is transmitted to the server when bookmarking or un-bookmarking
**Dependencies:** US-2, T-4
**Notes:** Ideen.md describes this as "Veranstaltung merken/folgen — rein lokal, kein Serverkontakt, kein Name nötig." Explicitly designed for two scenarios: (1) a guest who RSVPed on their phone and wants access on their laptop; (2) an undecided guest who wants to remember the event without committing. The bookmark is the prerequisite for the local event overview list (separate story). Because it is entirely client-side, it cannot be abused to fingerprint users. The event date is stored alongside the token and title so that US-7 (local event overview) can display it without making a server request. Locally cached title and date may become stale if the organizer edits the event — this is an acceptable trade-off for the fully-offline local overview; cached values are refreshed when the guest next visits the event page.
---
### US-7: Local event overview list
**As a** user,
**I want to** see a list of all events I have created, bookmarked, or RSVPed to on this device,
**so that** I can quickly navigate back to any event without having to find the original link again.
**Acceptance Criteria:**
- [ ] The root page (`/`) lists all events tracked locally on this device, below a project header/branding section
- [ ] An event appears in the list if it was created from this device (US-1, detected via organizer token in localStorage), bookmarked (US-6), or RSVPed from this device (US-3)
- [ ] Each entry shows at minimum: event title, date, and the user's relationship to the event (organizer / attending / not attending / bookmarked only)
- [ ] Each entry is a link that navigates directly to the event page
- [ ] The list is populated entirely from localStorage — no server request is made to render it
- [ ] Events whose date has passed are still shown in the list but visually distinguished (e.g. marked as "ended")
- [ ] If a user navigates to an event from the local overview and the server responds that the event no longer exists (deleted per US-12 or US-19), the app displays an "event no longer exists" message and offers to remove the entry from the local list
- [ ] If no events are tracked locally, an empty state is shown (not an error)
- [ ] An individual entry can be removed from the list: for bookmarked-only events this removes the bookmark; for RSVPed events it removes the local record (the server-side RSVP is unaffected); for organizer-created events it removes the local organizer token and event data
- [ ] When removing an organizer-created event entry, a confirmation warning is shown explaining that this will revoke organizer access on this device
- [ ] No personal data or event data is transmitted to the server when viewing or interacting with the overview
**Dependencies:** None
**Notes:** Ideen.md explicitly mentions "Übersichtsliste im LocalStorage: Alle Events die man zugesagt oder gemerkt hat (vgl. spliit)." Per Q-2 resolution: the overview lives at the root page `/` with a project header/branding above the event list. This feature is entirely client-side. Entries are populated from three localStorage sources: the organizer tokens (US-1), the bookmark set (US-6), and the RSVP records (US-3). All upstream stories store the event title and date in localStorage alongside their primary data, enabling this overview to render without any server contact. Locally cached values (title, date) may become stale if the organizer edits the event via US-5; stale data is refreshed when the user next visits the event page. Removing an entry from the local overview does not delete the server-side RSVP — that is intentional and consistent with the no-account design (the RSVP belongs to the organizer's data, not solely the guest's). Removing an organizer-created event entry removes the organizer token from localStorage, meaning the user loses organizer access on this device — the confirmation warning protects against accidental loss. Note: This story has no structural dependencies but requires the frontend scaffold from T-4 (which includes T-1) to be practically implementable. It is only meaningfully testable after US-1, US-3, or US-6 populate localStorage with event data — without those stories, the overview has nothing to display beyond the empty state.
---
### US-8: Add event to calendar
**As a** guest,
**I want to** add the event to my personal calendar,
**so that** I am reminded of it and always have the current date, time, and location at hand.
**Acceptance Criteria:**
- [ ] The event page provides a `.ics` file download link that generates a standards-compliant iCalendar (RFC 5545) file
- [ ] The `.ics` file includes: event title, description (if present), start date and time, location (if present), the public event URL, and a unique UID derived from the event token
- [ ] The `.ics` file is generated and served server-side; downloading it does not require JavaScript
- [ ] The event page also provides a `webcal://` subscription link so that calendar applications can subscribe and receive automatic updates when the event is edited (US-5)
- [ ] The `webcal://` endpoint serves the identical iCalendar content as the `.ics` download, using the same event token in the URL
- [ ] Both links are available to any visitor holding the event link — no RSVP, login, or personal data required
- [ ] No personal data, name, or IP address is logged when either link is accessed
- [ ] If the event has expired, both links remain available so the guest can still obtain the calendar record
- [ ] If the event has been cancelled (US-18), the `.ics` file and `webcal://` feed include `STATUS:CANCELLED` so that subscribed calendar applications reflect the cancellation on their next sync [deferred until US-18 is implemented]
**Dependencies:** US-2, T-4
**Notes:** Ideen.md specifies "Kalender-Integration: .ics-Download + optional webcal:// für Live-Updates bei Änderungen." The `webcal://` subscription is especially valuable alongside US-5 (Edit event details): when the organizer updates the date or location, subscribed guests see the change reflected in their calendar on the next sync without having to revisit the event page. The UID in the `.ics` file must be stable across regenerations (derived from the event token) so that calendar applications update the existing entry rather than creating duplicates.
---
### US-9: Highlight changed event details
**As a** guest,
**I want to** see which event details have changed since I last visited the event page,
**so that** I immediately notice important updates like a rescheduled date, a new time, or a changed location.
**Acceptance Criteria:**
- [ ] When the organizer saves an edit (US-5), the server records which fields changed (title, description, date/time, location) and stores the timestamp of that edit alongside the event
- [ ] When a guest opens the event page, any field that was modified in the most recent organizer edit is visually highlighted (e.g. a "recently changed" indicator next to the field)
- [ ] The highlight is only shown to guests who have not visited the event page since the most recent edit — determined by comparing the event's `last_edited_at` timestamp against a `last_seen_at` value stored in localStorage per event token
- [ ] On first visit (no `last_seen_at` in localStorage), no highlight is shown — the event is new to the guest, so highlighting individual fields would be misleading
- [ ] After the event page is rendered, the guest's `last_seen_at` in localStorage is updated to match the current `last_edited_at`, so the highlight disappears on the next visit
- [ ] The highlight mechanism is entirely client-side: the `last_seen_at` timestamp is stored and read locally; no visit data is transmitted to the server
- [ ] If the organizer makes multiple successive edits, only the fields changed in the most recent edit are highlighted; earlier intermediate changes are not tracked
- [ ] If the event has not been edited since creation, no highlights are shown
**Dependencies:** US-2, US-5, T-4
**Notes:** Ideen.md specifies "Änderungen zum ursprünglichen Inhalt (z.b. geändertes datum/ort) werden iwi hervorgehoben." The comparison is against the most recent edit (not the original creation values) — simpler and more actionable for guests. Storing the set of changed field names server-side (alongside `last_edited_at`) is necessary because the client cannot reconstruct which fields changed from timestamps alone. The highlight logic runs client-side using only locally stored state; no server round-trip is required beyond the normal event page load.
---
### US-10a: Post update messages as organizer
**As an** event organizer,
**I want to** post short update messages on the event page and manage them,
**so that** guests are informed of announcements or notes without requiring a separate communication channel.
**Acceptance Criteria:**
- [ ] From the organizer view, the organizer can compose and submit a plain-text update message
- [ ] Each submitted message is stored server-side, associated with the event, and timestamped at the time of posting
- [ ] All update messages for an event are displayed on the public event page in reverse chronological order (newest first), each with a human-readable timestamp
- [ ] Update messages cannot be posted after the event's expiry date
- [ ] The organizer can delete any previously posted update message from the organizer view; deletion is permanent and the message is immediately removed from the public event page
- [ ] If the organizer token is absent or invalid, the compose and delete UI is not shown and the server rejects any attempt to post or delete update messages
- [ ] No account or additional authentication step is required beyond the organizer token
- [ ] No personal data or IP address is logged when update messages are fetched or posted
**Dependencies:** US-1, US-2, T-4
**Notes:** Ideen.md specifies "Veranstalter kann Updatenachrichten im Event posten." This story covers the server-side feature: posting, displaying, and deleting update messages. The client-side read-state tracking (badge/indicator for new updates) is a separate concern covered in US-10b. Per Q-3 resolution: the app is a SPA; JavaScript-dependent rendering is acceptable for update messages. Cancelled events (US-18): posting update messages is not blocked by cancellation, only by expiry (AC 4). This is intentional — the organizer may want to post post-cancellation communication (e.g. a rescheduling notice or explanation). The cancellation message (US-18) is a static one-time message, while update messages are a stream of announcements serving a different purpose.
---
### US-10b: New-update indicator for guests
**As a** guest,
**I want to** see a visual indicator when there are update messages I haven't seen yet,
**so that** I immediately notice new announcements without having to read through all messages.
**Acceptance Criteria:**
- [ ] Guests who open the event page and have unread updates (i.e. updates posted since their last visit) see a visual indicator — a badge or highlighted section — drawing attention to the new messages
- [ ] "Read" state is tracked entirely in localStorage: on page load, the timestamp of the newest update is compared to a `updates_last_seen_at` value stored locally per event token; if the update is newer, the indicator is shown
- [ ] After the event page is rendered, `updates_last_seen_at` in localStorage is set to the current latest update timestamp, so the indicator clears on the next visit
- [ ] On first visit (no `updates_last_seen_at` in localStorage), no "new update" indicator is shown; updates are displayed as-is without a "new" badge
- [ ] No server request is made to record that a guest read the updates — tracking is purely local
**Dependencies:** US-10a
**Notes:** Ideen.md specifies "pro Device wird via LocalStorage gemerkt was man schon gesehen hat (Badge/Hervorhebung für neue Updates)." This story is the client-side read-state complement to US-10a (which covers posting, displaying, and deleting messages). The first-visit exclusion (no badge on first open) is intentional — a guest who has never seen the event before would find all updates misleading to label as "new". The `updates_last_seen_at` key is separate from the `last_seen_at` key used in US-9. This story is distinct from US-9 (change highlighting for edited event fields): US-9 highlights structural field changes (date, location, title), while this story covers awareness of new free-form announcements.
---
### US-11: Generate a QR code for an event
**As an** event organizer,
**I want to** generate and download a QR code for my event,
**so that** I can print it on posters or flyers and let people access the event page by scanning it.
**Acceptance Criteria:**
- [ ] The event page displays a QR code that encodes the public event URL
- [ ] The QR code is generated entirely server-side — no external QR code service is called
- [ ] The QR code is downloadable as a file suitable for printing (e.g. SVG or high-resolution PNG)
- [ ] The QR code download is a direct link to a server endpoint — the actual file download does not require client-side generation
- [ ] The QR code is accessible to any visitor holding the event link, not only the organizer
- [ ] No personal data, IP address, or identifier is transmitted to any third party when the QR code is generated or downloaded
- [ ] The QR code remains available and downloadable after the event has expired
**Dependencies:** US-2, T-4
**Notes:** Ideen.md specifies "QR Code generieren (z.B. für Plakate/Flyer)." The QR code must be server-side generated — calling an external service would violate the no-external-dependencies-that-phone-home statute. The code encodes only the public event URL; no additional metadata is embedded. Making it available to all visitors (not just the organizer) reflects the use case: the organizer can hand printed material to guests, and guests who received a physical flyer can share the event link digitally by re-scanning. Per Q-3 resolution: the app is a SPA; client-side rendering of the QR code display is acceptable. The download mechanism remains a direct server endpoint link.
---
### US-12: Automatic data deletion after expiry date
**As a** guest,
**I want** all event data — including my RSVP and any other stored personal information — to be automatically and permanently deleted after the event's expiry date,
**so that** I can trust that data I submitted is not retained on the server longer than necessary.
**Acceptance Criteria:**
- [ ] The server runs a periodic cleanup process that deletes all data associated with events whose expiry date has passed
- [ ] The cleanup deletes the event record itself along with all associated RSVPs, update messages (US-10a), field-change metadata (US-9), stored header images (US-16) [deferred until US-16 is implemented], and cancellation state (US-18 if applicable)
- [ ] After deletion, the event's public URL returns a clear "event not found" response — no partial data is ever served
- [ ] The cleanup process runs automatically without manual operator intervention (e.g. a scheduled job or on-request lazy cleanup triggered by access attempts)
- [ ] No log entry records the names, RSVPs, or any personal data of the deleted event's guests — the deletion is silent from a logging perspective
- [ ] Extending the expiry date via US-5 (before it has passed) delays the deletion accordingly — the cleanup always uses the current stored expiry date
- [ ] The cleanup is not triggered early: data is retained until the expiry date has passed, not before
**Dependencies:** US-1, T-4
**Notes:** Ideen.md specifies "Ablaufdatum als Pflichtfeld, nach dem alle gespeicherten Daten gelöscht werden." This is a privacy guarantee, not merely a housekeeping task. The mandatory expiry date in US-1 is only meaningful if the server actually enforces deletion. The implementation strategy (scheduled cron job, lazy cleanup on access, or both) is an architectural decision to be made during implementation. What matters at the story level is the observable behavior: data is gone after expiry, and no residual records remain. LocalStorage entries on guests' devices are unaffected by server-side deletion — that is intentional and consistent with the client-side-only nature of localStorage.
---
### US-13: Limit the number of active events per instance
**As a** self-hoster,
**I want to** configure a maximum number of simultaneously active events via a server environment variable,
**so that** I can prevent storage exhaustion and limit potential abuse on my instance without modifying code.
**Acceptance Criteria:**
- [ ] The server reads a configurable environment variable (e.g. `MAX_ACTIVE_EVENTS`) at startup to determine the event cap
- [ ] If the configured limit is reached, any attempt to create a new event is rejected with a clear error response indicating the instance is at capacity
- [ ] The error is surfaced to the user on the event creation form — not as a silent failure
- [ ] If the environment variable is unset or empty, no limit is applied (unlimited events by default, suitable for personal or trusted-group instances)
- [ ] Only non-expired events count toward the limit; expired events awaiting cleanup are not counted
- [ ] The limit is enforced server-side; it cannot be bypassed by the client
- [ ] No personal data is logged when the limit is hit — only the rejection response is returned
**Dependencies:** US-1, T-4
**Notes:** Ideen.md lists "Max aktive Events als serverseitige Konfiguration (env variable)" under security/abuse-prevention measures. This is a deployment-time configuration intended for the self-hoster, not a user-facing feature in the traditional sense. The story is written from the self-hoster's perspective because they are the ones who configure and benefit from this capability. The environment variable approach aligns with the Dockerfile-based deployment model described in CLAUDE.md. Only non-expired events count toward the limit — consistent with US-12 (expired events are deleted and must not permanently consume capacity).
---
### US-14: Install as Progressive Web App
**As a** guest,
**I want to** install the app on my device from the browser,
**so that** it feels like a native app and I can launch it directly from my home screen.
**Acceptance Criteria:**
- [ ] The app serves a valid web app manifest with at minimum: app name, icons in multiple sizes, standalone display mode, theme color, and a start URL
- [ ] The app meets browser installability requirements (manifest + registered service worker) so that the browser's "Add to Home Screen" / install prompt is available on supported mobile and desktop browsers
- [ ] When launched from the home screen, the app opens in standalone mode — without browser address bar or navigation chrome
- [ ] The app displays the configured icon and name on the device's home screen and in the OS app switcher
- [ ] On repeat visits, previously loaded pages and app assets load quickly due to service worker caching
- [ ] No external resources are fetched by the manifest or service worker — all assets are self-hosted
- [ ] The manifest's start URL points to the root page (`/`), which serves the local event overview (US-7), so returning users see their tracked events immediately
**Dependencies:** T-4
**Notes:** Ideen.md states "Soll als PWA im Browser laufen / Damit es sich wie eine normale app anfühlt." The PWA requirement is about installability and native-app feel — the app should be indistinguishable from a native app when launched from the home screen. Per Q-2 resolution: the local event overview lives at `/` and serves as the start URL. Per Q-3 resolution: the app is a SPA with a RESTful API; the service worker caching strategy will be determined during implementation. Note: While this story depends only on T-4 structurally, the service worker and manifest are only meaningfully testable after other stories (e.g. US-2, US-7) provide actual pages and assets to cache and serve from the home screen.
---
### US-15: Choose event color theme
**As an** event organizer,
**I want to** choose a visual color theme for my event page,
**so that** the event page reflects the mood or style of the event and stands out visually.
**Acceptance Criteria:**
- [ ] During event creation or editing (US-5), the organizer can select from a set of predefined color themes (e.g. color schemes or visual styles)
- [ ] A default theme is applied if the organizer makes no selection
- [ ] Theme selection is persisted server-side alongside the event data
- [ ] The guest-facing event page renders with the selected color theme
- [ ] Themes affect only the individual event page, not the app's global UI (navigation, local overview, forms)
- [ ] The customization UI is part of the event creation and editing forms
- [ ] No external resources are required for any predefined theme — all styles are self-contained
**Dependencies:** US-1, US-2, T-4
**Notes:** Ideen.md mentions "Irgendwie auch Designbar, sofern man das will." Per Q-1 resolution: predefined themes for event pages. This is event-level styling — each event can have its own visual identity. The app's global appearance (including dark/light mode, US-17) is a separate concern. No external service or API key is needed for predefined themes. The interaction between event-level color themes and the app-level dark/light mode (US-17) must be considered during implementation: predefined themes should remain readable and visually coherent regardless of whether the user has dark or light mode active on the surrounding app chrome (see also US-17 notes).
---
### US-16: Select event header image from Unsplash
**As an** event organizer,
**I want to** search for and select a header image for my event page via an integrated image search,
**so that** the event page has a visually appealing header that matches the event's theme.
**Acceptance Criteria:**
- [ ] During event creation or editing (US-5), the organizer can search for a header image via an integrated Unsplash search
- [ ] The Unsplash search is server-proxied: the client sends the search query to the app's backend, which calls the Unsplash API and returns results — the client never contacts Unsplash directly
- [ ] When an image is selected, the server downloads and stores the image locally on disk; it is served from the app's own domain, not from Unsplash's CDN
- [ ] Proper Unsplash attribution (photographer name, link to Unsplash) is displayed alongside the header image on the event page, as required by the Unsplash API terms
- [ ] The organizer can remove a previously selected header image
- [ ] The guest-facing event page renders with the selected header image (if set)
- [ ] No guest data, IP address, or identifier is transmitted to Unsplash or any third party from the guest's browser
- [ ] If the server has no Unsplash API key configured, the image search feature is unavailable — the option is simply not shown, no error
- [ ] If the API key is removed from the config after images were already stored, existing images continue to render from disk; only the search/select UI becomes unavailable; the event page never breaks due to a missing API key
- [ ] When the event expires and is deleted (US-12), the stored image file is deleted along with all other event data
- [ ] The hoster must mount a persistent volume for the image storage directory (documented in README)
**Dependencies:** US-1, US-2, T-4
**Notes:** Per Q-1 resolution: Unsplash image search as an optional feature alongside predefined themes (US-15). The Unsplash integration is privacy-safe because it is server-proxied: guests never contact Unsplash; the server fetches and stores images locally. The Unsplash API requires attribution (photographer name and Unsplash link) — this is a legal/terms requirement. The Unsplash API key is an optional deployment configuration for the self-hoster; if not configured, only predefined themes (US-15) are available. Image storage on disk requires the hoster to configure a persistent volume — standard Docker practice, documented in README alongside the docker-compose example.
---
### US-17: Dark/light mode
**As a** user,
**I want to** switch between dark and light mode for the app's interface,
**so that** I can use the app comfortably in different lighting conditions and according to my personal preference.
**Acceptance Criteria:**
- [ ] The app respects the user's operating system / browser preference (`prefers-color-scheme`) as the default mode on first visit
- [ ] A visible toggle allows the user to manually switch between dark and light mode
- [ ] The user's manual preference is stored in localStorage and takes precedence over the system preference on subsequent visits
- [ ] The toggle is accessible from any page in the app (e.g. in a header or navigation element)
- [ ] Dark/light mode affects the app's global UI: navigation, local event overview, forms, and all non-event-page chrome
- [ ] Event pages use their own color theme (US-15) which is independent of the app-level dark/light mode
- [ ] The mode switch is purely client-side — no server request is made, no preference data is transmitted
- [ ] Both modes meet accessibility contrast requirements (WCAG AA minimum)
**Dependencies:** None
**Notes:** This is app-level theming — completely separate from the event-level color themes (US-15). Dark/light mode affects the overall UI (navigation, local overview, forms, etc.), not individual event pages. The interaction between app-level dark/light mode and event-level color themes (US-15) should be considered during implementation to ensure readability in both modes. Note: This story has no structural dependencies but requires the frontend scaffold from T-4 (which includes T-1) to be practically implementable.
---
### US-18: Cancel an event as organizer
**As an** event organizer,
**I want to** explicitly cancel my event and optionally provide a reason,
**so that** guests clearly see the event is cancelled and understand why.
**Acceptance Criteria:**
- [ ] The organizer view provides a dedicated "Cancel event" action, separate from editing event details (US-5)
- [ ] When cancelling, the organizer can optionally enter a cancellation message (reason/explanation)
- [ ] When cancelling, the organizer can optionally adjust the event's expiry date (to control how long the cancellation notice remains visible before data deletion per US-12); the adjusted date must be in the future — consistent with US-5's expiry date constraint and US-19's role as the immediate-removal mechanism
- [ ] A confirmation step is required before cancellation is finalized
- [ ] After cancellation, the event page clearly displays a "cancelled" state with the cancellation message if provided (US-2)
- [ ] RSVPs are no longer possible on a cancelled event — the RSVP form is hidden and the server rejects any RSVP submissions (US-3)
- [ ] The event remains visible and accessible via its link until its expiry date, so guests can still see what happened and why
- [ ] After cancellation, the organizer can still edit the cancellation message but cannot "un-cancel" the event
- [ ] Cancellation is only accessible when a valid organizer token for the event is present in localStorage
- [ ] If the organizer token is absent or invalid, the cancel action is not shown and the server rejects cancellation requests
- [ ] The cancellation state and message are persisted server-side
- [ ] No account or additional authentication step is required beyond the organizer token
**Dependencies:** US-1, T-4
**Notes:** The overseer identified that cancellation was previously conflated with shortening the expiry date in US-5, which was unintuitive and conflated two fundamentally different actions. Editing the expiry date (US-5) is now purely an edit operation; cancellation is an explicit, dedicated action. The event remains visible after cancellation (unlike immediate deletion) until the expiry date passes, giving guests time to see the cancellation notice. The optional expiry date adjustment during cancellation lets the organizer control how long the notice stays visible — e.g. keep it up for a week so everyone sees it, or shorten it to trigger faster cleanup. The "no un-cancel" constraint keeps the model simple: cancellation is a one-way state transition. If the organizer made a mistake, they can create a new event.
---
### US-19: Delete an event as organizer
**As an** event organizer,
**I want to** immediately and permanently delete my event and all its data,
**so that** I can remove an event entirely when it was created by mistake or is no longer needed.
**Acceptance Criteria:**
- [ ] The organizer view provides a dedicated "Delete event" action, separate from editing (US-5) and cancelling (US-18)
- [ ] A confirmation warning is displayed before deletion, clearly stating that the action is immediate, permanent, and irreversible — all event data including RSVPs, update messages, and images will be lost
- [ ] Upon confirmation, the server permanently deletes the event record and all associated data: RSVPs, update messages (US-10a), field-change metadata (US-9), stored header images (US-16), and cancellation state (US-18 if applicable)
- [ ] After deletion, the event's public URL returns a "event not found" response (consistent with US-2 and US-12 behavior)
- [ ] After successful deletion, the app removes the event's organizer token and metadata from localStorage and redirects the organizer to the root page (`/`)
- [ ] Deletion is accessible only when a valid organizer token for the event is present in localStorage
- [ ] If the organizer token is absent or invalid, the delete action is not shown and the server rejects deletion requests
- [ ] No personal data or event data is logged during deletion — the deletion is silent from a logging perspective
- [ ] No account or additional authentication step is required beyond the organizer token
- [ ] The event can be deleted regardless of its current state (active, cancelled, or expired)
**Dependencies:** US-1, T-4
**Notes:** The overseer identified that using the expiry date as a deletion mechanism (setting it to today or a past date in US-5) was unintuitive and conflated two different actions. US-5 now enforces that the expiry date can only be set to a future date. If the organizer wants the event gone immediately, they use this explicit deletion feature. Unlike cancellation (US-18), which keeps the event visible with a cancellation notice until the expiry date, deletion removes the event entirely and immediately. This is the organizer's "nuclear option" — useful when the event was created by mistake, contains wrong information, or is no longer needed at all. The deletion behavior is identical to what US-12 does automatically after expiry, but triggered manually and immediately by the organizer.