Files
fete/.ralph/story-refinement/progress.txt
nitrix 7b460dd322 initial commit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:03:47 +01:00

161 lines
29 KiB
Plaintext

# 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 ✓.