From c07687102e98d8296c7eb2543f7e5f29755a862b Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Mon, 5 Jan 2026 12:25:33 +0100 Subject: [PATCH] bd sync: 2026-01-05 12:25:28 --- .beads/issues.jsonl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index d88d121..2ece200 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -3,8 +3,10 @@ {"id":"fotospiel-app-0h0","title":"SEC-BILL-02 Signature freshness + retry policies for Paddle webhooks","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:53:37.618780852+01:00","created_by":"soeren","updated_at":"2026-01-01T15:53:37.618780852+01:00"} {"id":"fotospiel-app-0rb","title":"Tenant admin onboarding: inline checkout integration in welcome flow","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:08:22.434997456+01:00","created_by":"soeren","updated_at":"2026-01-01T16:08:28.026795975+01:00","closed_at":"2026-01-01T16:08:28.026795975+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-0u0","title":"Paddle migration: design Paddle data mappings for packages/products/prices","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:15.991704177+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:21.629616074+01:00","closed_at":"2026-01-01T15:57:21.629616074+01:00","close_reason":"Completed in codebase (verified)"} +{"id":"fotospiel-app-1eu","title":"Live Show: decide queue model \u0026 multi-screen ordering","description":"# Decision: Queue model \u0026 multi-screen ordering\n\n## Context\nThe Live Show player needs a steady stream of approved photos.\n\nConstraints:\n- High volume bursts (dozens/minute).\n- Multiple display devices may be used (projector + TV).\n- We want a simple, resilient model that supports reconnect/resync.\n\n## Options\n### A) Derived queue from `photos` + stable ordering (recommended for MVP)\n- Store Live Show eligibility on each photo (`live_status=approved`, `live_approved_at`, etc.).\n- The server provides an **ordered feed** by `(live_approved_at, id)` (or by a monotonic sequence).\n- The player maintains its own playback queue and “recently shown” cooldown in memory.\n\n**Pros**\n- Minimal schema complexity.\n- Easy resync: fetch “approved since cursor”.\n- Works well with realtime events + polling fallback.\n\n**Cons**\n- Playback is not perfectly deterministic across multiple displays (they may be at different offsets), unless we add a shared cursor/session.\n\n### B) Explicit queue table (deterministic)\n- Introduce `live_show_queue_entries` with an ordered list, plus `played_at`, `skipped_at`, `position`.\n- Player reads from server-defined queue, optionally per “show session”.\n\n**Pros**\n- Deterministic ordering across screens.\n- Great for analytics (“what was shown when”).\n\n**Cons**\n- More complex writes and concurrency.\n- Requires extra rules for retention/pruning and backpressure.\n\n### C) Server-driven playback session\n- Server owns playback ticks and pushes “now playing” to all screens.\n\n**Pros**\n- Perfect sync.\n\n**Cons**\n- Overkill; introduces tight coupling, more infra and failure points.\n\n## Recommended decision\nChoose **Option A** for MVP.\n\n### Ordering recommendation\nUse stable ordering `ORDER BY live_approved_at ASC, id ASC` (or DESC depending on query usage).\n\nIf we later need strict determinism across displays, add one of:\n- A monotonic per-event `live_sequence` assigned at approval time (stored on the photo), or\n- A queue table (Option B).\n\n## Multi-screen behavior (expected)\n- Multiple displays are supported; they will show the **same set** of approved photos.\n- They are not guaranteed to be frame-synchronized.\n- New photos should appear on all screens shortly after approval.\n\n## API/feed shape (to guide downstream work)\n- Player boot: `GET /api/v1/live-show/{token}/photos?limit=50` (latest approved within retention window).\n- Incremental resync: `GET .../photos?after_id=...\u0026limit=200` or `after_approved_at=...`.\n- Realtime event includes `photo_id` and enough metadata to enqueue, with client fetching the image asset.\n\n## Edge cases \u0026 rules\n- **Burst control**: if \u003eN new photos arrive quickly, player shows a “grid burst” or increases pace temporarily.\n- **No new photos**: player rotates through backlog (curated/balanced modes) without repeating too frequently.\n- **Moderation reversal**: if a photo becomes rejected, it is removed from future rotation (do not hard-cut current frame).\n\n## Decision needed from product\n- Is “not perfectly synchronized across multiple screens” acceptable for V1? (recommended: yes)\n- Do we need analytics of exactly what was shown on screen? (if yes, consider Option B)\n","acceptance_criteria":"- Options A/B/C compared with pros/cons\\n- Clear recommendation (derived queue) and stable ordering defined\\n- Expected multi-screen behavior documented\\n- Product questions captured (sync requirement, analytics)","notes":"Decision: V1 uses derived queue from photos with stable ordering; multi-screen is allowed but not frame-synchronized (acceptable).","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-05T11:43:21.963371839+01:00","created_by":"soeren","updated_at":"2026-01-05T12:06:45.95299192+01:00","closed_at":"2026-01-05T12:06:45.95299192+01:00","close_reason":"Closed","dependencies":[{"issue_id":"fotospiel-app-1eu","depends_on_id":"fotospiel-app-vro","type":"blocks","created_at":"2026-01-05T11:43:52.783766767+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-1hh","title":"Coupon ops: expand fraud tooling with IP/device reputation","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:09:33.355825022+01:00","created_by":"soeren","updated_at":"2026-01-02T23:28:08.628957239+01:00","closed_at":"2026-01-02T23:28:08.628957239+01:00","close_reason":"Closed"} {"id":"fotospiel-app-1md","title":"Paddle catalog sync: SyncPackageToPaddle job create/update","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:36.196972205+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:41.854731591+01:00","closed_at":"2026-01-01T16:00:41.854731591+01:00","close_reason":"Completed in codebase (verified)"} +{"id":"fotospiel-app-1we","title":"Live Show: define trusted uploader rules \u0026 default retention window","description":"# Decision: Trusted uploader rules \u0026 default retention window\n\n## Context\nModeration is required for many events, but we also want a fast “auto-approve trusted sources” mode.\n\nWe currently track photo ingestion sources in `photos.ingest_source` (e.g. `tenant_admin`, `photobooth`, `sparkbooth`, `guest_pwa`). Guest uploads are token-based and do not have strong identity guarantees.\n\n## Definitions\n- **Trusted uploader**: uploads that can bypass Live Show manual moderation.\n- **Retention window**: time window for which approved photos remain eligible for rotation in the Live Show.\n\n## Options (trusted rules)\n### A) Trust by ingestion source only (recommended for V1)\nAuto-approve for Live Show only when `ingest_source` is one of:\n- `tenant_admin` (authenticated staff actions)\n- `photobooth` / `sparkbooth` (controlled integrations)\n\nAll `guest_pwa` uploads require manual approval when moderation is enabled.\n\n**Pros**\n- Harder to spoof; aligns with real security boundaries.\n- Simple to explain and operate.\n\n**Cons**\n- Guests never auto-approve; more moderator work.\n\n### B) Trust by guest device id (not recommended without stronger proof)\nUse `created_by_device_id` / `X-Device-Id` to whitelist devices.\n\n**Risk**\n- Device IDs are not cryptographically bound; a motivated guest could spoof the header.\n\nIf we want this later, we should introduce a **server-issued signed device token** (pairing flow) and validate it on upload.\n\n### C) Trust by invitation/QR (future)\nGuests who joined with a special “staff QR/pairing token” become trusted.\n\n## Recommended decision\nChoose **Option A** for V1.\n\n### Moderation mode semantics (proposed)\n- `off`: all photos with “submit to live show” become `approved` immediately *except* photos that are already flagged/removed by other moderation pipelines.\n- `manual`: all guest PWA photos become `pending`; trusted sources auto-approve.\n- `trusted_only`: same as manual, but UI copy emphasises that only booth/staff are automatic.\n\n## Retention window (defaults)\n### Recommendation\nDefault `retention_window_hours = 12` (configurable per event).\n\nRationale:\n- Keeps the “eligible set” bounded for performance.\n- Fits most event durations; avoids showing very old photos late in the night.\n\n### Notes\n- Even with a retention window, we can still show older photos via “curated” mode (e.g. featured/top-liked) if product wants.\n\n## Edge cases\n- **High-volume**: moderators may not keep up → allow temporary switch to “trusted_only” + announce to guests.\n- **Abuse**: if a trusted integration misbehaves, operator can disable trusted auto-approve.\n- **Reversal**: approving a previously rejected photo must be tracked with audit info (who/when).\n\n## Decision needed from product\n- Confirm the default retention window: 12h vs 6h vs “entire event”.\n- Confirm whether “trusted_only” should auto-approve `tenant_admin` uploads (recommended: yes).\n- Confirm whether guest auto-approve is desired in V1 (recommended: no, unless we build pairing).\n","acceptance_criteria":"- Trusted rules options listed, with security risk called out for device-id trust\\n- Clear V1 recommendation (trust by ingest_source only)\\n- Moderation mode semantics defined\\n- Default retention window recommendation + product decision questions","notes":"Decision: V1 trusted auto-approve uses ingest_source only (tenant_admin/photobooth/sparkbooth). Default retention_window_hours = 12.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-05T11:43:32.455339503+01:00","created_by":"soeren","updated_at":"2026-01-05T12:06:45.973092473+01:00","closed_at":"2026-01-05T12:06:45.973092473+01:00","close_reason":"Closed","dependencies":[{"issue_id":"fotospiel-app-1we","depends_on_id":"fotospiel-app-vro","type":"blocks","created_at":"2026-01-05T11:44:02.062725386+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-25q","title":"Security review: payments/webhooks code audit (signatures, idempotency, linkage)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:25.747336642+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:25.747336642+01:00"} {"id":"fotospiel-app-29o","title":"Paddle catalog sync: PackageResource sync status badges + timestamp","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:01:10.009385187+01:00","created_by":"soeren","updated_at":"2026-01-01T16:01:15.639525807+01:00","closed_at":"2026-01-01T16:01:15.639525807+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-2hq","title":"Security review: marketing/API controller+validation review","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:08.862737923+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:08.862737923+01:00"} @@ -15,11 +17,14 @@ {"id":"fotospiel-app-4ar","title":"SEC-BILL-03 Failed capture notifications + ledger hook","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:54:33.266516715+01:00","created_by":"soeren","updated_at":"2026-01-01T15:54:33.266516715+01:00"} {"id":"fotospiel-app-4i4","title":"Security review: map roles/data","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:58.370301875+01:00","created_by":"soeren","updated_at":"2026-01-01T16:03:03.997327414+01:00","closed_at":"2026-01-01T16:03:03.997327414+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-4zu","title":"SEC-IO-02 Refresh-token management UI + audit logs","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:51:50.24186222+01:00","created_by":"soeren","updated_at":"2026-01-04T16:10:39.752587431+01:00","closed_at":"2026-01-04T16:10:39.752587431+01:00","close_reason":"Obsolete: authentication now uses Sanctum PATs; OAuth/refresh-token tables removed and no refresh-token flow remains. See docs/archive/prp/13-backend-authentication.md and docs/archive/prp/marketing-checkout-payment-architecture.md."} +{"id":"fotospiel-app-539","title":"Live Show: public player view with effects engine","status":"open","priority":1,"issue_type":"feature","created_at":"2026-01-05T11:11:36.821959901+01:00","created_by":"soeren","updated_at":"2026-01-05T11:11:36.821959901+01:00","dependencies":[{"issue_id":"fotospiel-app-539","depends_on_id":"fotospiel-app-qne","type":"blocks","created_at":"2026-01-05T11:12:58.721858159+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-539","depends_on_id":"fotospiel-app-6zc","type":"blocks","created_at":"2026-01-05T11:13:07.289796993+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-539","depends_on_id":"fotospiel-app-h5d","type":"blocks","created_at":"2026-01-05T11:44:42.719445471+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-55n","title":"Tenant admin onboarding: add Paddle error UX + test coverage","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:08:40.463283816+01:00","created_by":"soeren","updated_at":"2026-01-01T16:08:40.463283816+01:00"} {"id":"fotospiel-app-574","title":"Paddle catalog sync: extend PaddleClient tests/mocks for catalog endpoints","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:03.486301225+01:00","created_by":"soeren","updated_at":"2026-01-02T21:11:39.626820206+01:00","closed_at":"2026-01-02T21:11:39.626820206+01:00","close_reason":"Deprioritized"} {"id":"fotospiel-app-576","title":"Tenant admin onboarding: legacy asset audit + component inventory","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:07:59.996563146+01:00","created_by":"soeren","updated_at":"2026-01-01T16:08:05.599274641+01:00","closed_at":"2026-01-01T16:08:05.599274641+01:00","close_reason":"Completed in codebase (verified)"} +{"id":"fotospiel-app-579","title":"Live Show: tests (backend + UI smoke)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-05T11:11:57.246607374+01:00","created_by":"soeren","updated_at":"2026-01-05T11:11:57.246607374+01:00","dependencies":[{"issue_id":"fotospiel-app-579","depends_on_id":"fotospiel-app-539","type":"blocks","created_at":"2026-01-05T11:13:27.729131522+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-579","depends_on_id":"fotospiel-app-xg5","type":"blocks","created_at":"2026-01-05T11:13:37.425191011+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-579","depends_on_id":"fotospiel-app-qne","type":"blocks","created_at":"2026-01-05T11:13:46.257175231+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-5dl","title":"Paddle catalog sync: PaddleCatalogService scaffold","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:24.916655836+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:30.566084195+01:00","closed_at":"2026-01-01T16:00:30.566084195+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-5hk","title":"Fix staging coupon seed 500 for E2E","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-03T15:12:53.643644221+01:00","created_by":"soeren","updated_at":"2026-01-04T16:21:46.441797374+01:00","closed_at":"2026-01-04T16:21:46.441797374+01:00","close_reason":"Resolved elsewhere; staging coupon seed 500 no longer reproducible after recent backend changes."} +{"id":"fotospiel-app-5ie","title":"Help docs: Live Show how-to + recommended hardware (DE/EN)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-05T11:12:05.973844187+01:00","created_by":"soeren","updated_at":"2026-01-05T11:30:44.911207413+01:00","dependencies":[{"issue_id":"fotospiel-app-5ie","depends_on_id":"fotospiel-app-vro","type":"blocks","created_at":"2026-01-05T11:13:54.925412888+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-5ie","depends_on_id":"fotospiel-app-539","type":"blocks","created_at":"2026-01-05T11:14:03.257649076+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-5iy","title":"Security review: confirm env/header defaults","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:03:20.808188183+01:00","created_by":"soeren","updated_at":"2026-01-01T16:03:26.388002115+01:00","closed_at":"2026-01-01T16:03:26.388002115+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-5s3","title":"Localized SEO: canonical/hreflang tags + localized navigation","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:03.909947355+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:09.550647107+01:00","closed_at":"2026-01-01T16:02:09.550647107+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-5zl","title":"Ensure checkout step 3 requires login for Paddle checkout","description":"Problem: Paddle checkout on step 3 fails when user is not logged in. Step 3 must enforce authentication before initializing Paddle checkout.\\n\\nSuggestions:\\n- Protect step 3 route/controller with auth middleware and redirect to login with intended return URL.\\n- Gate step 3 UI/CTA on auth state; show inline login prompt and disable Paddle until authenticated.\\n- Require auth in backend endpoint that creates Paddle transaction/session; return 401 and send user to login.\\n- Optionally preflight at end of step 2 to prompt login before advancing.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-04T12:31:43.215017311+01:00","created_by":"soeren","updated_at":"2026-01-04T12:42:45.088723058+01:00","closed_at":"2026-01-04T12:42:45.088723058+01:00","close_reason":"Closed"} @@ -27,6 +32,7 @@ {"id":"fotospiel-app-6dp","title":"Coupon ops enhancements (redemption service, preview endpoint, widget, export)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:09:09.275919717+01:00","created_by":"soeren","updated_at":"2026-01-01T16:09:14.882264149+01:00","closed_at":"2026-01-01T16:09:14.882264149+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-6oj","title":"Security review: media pipeline code audit (AV/EXIF, signed URLs, storage separation)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:31.390878341+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:31.390878341+01:00"} {"id":"fotospiel-app-6yt","title":"Paddle migration: register sandbox webhooks + document events consumed","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:56:34.333714988+01:00","created_by":"soeren","updated_at":"2026-01-02T22:23:52.212191068+01:00","closed_at":"2026-01-02T22:23:52.212191068+01:00","close_reason":"Completed"} +{"id":"fotospiel-app-6zc","title":"Live Show: Admin app settings \u0026 effect presets","status":"open","priority":2,"issue_type":"feature","created_at":"2026-01-05T11:11:27.038815978+01:00","created_by":"soeren","updated_at":"2026-01-05T11:11:27.038815978+01:00","dependencies":[{"issue_id":"fotospiel-app-6zc","depends_on_id":"fotospiel-app-vro","type":"blocks","created_at":"2026-01-05T11:12:50.048055484+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-7bu","title":"Paddle migration: extend config/env handling for Paddle keys/webhook secrets","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:27.242854801+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:32.890355888+01:00","closed_at":"2026-01-01T15:57:32.890355888+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-7u1","title":"Paddle catalog sync: PaddlePackagePull job","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:47.468892178+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:53.126602817+01:00","closed_at":"2026-01-01T16:00:53.126602817+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-95m","title":"Paddle migration: admin catalog sync UI for packages","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:49.790409261+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:55.418180246+01:00","closed_at":"2026-01-01T15:57:55.418180246+01:00","close_reason":"Completed in codebase (verified)"} @@ -57,10 +63,12 @@ {"id":"fotospiel-app-dn3","title":"SEC-IO-03 Subnet/device matching config + tests","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:51:55.917743667+01:00","created_by":"soeren","updated_at":"2026-01-04T16:10:50.464423305+01:00","closed_at":"2026-01-04T16:10:50.464423305+01:00","close_reason":"Obsolete: subnet/device matching was tied to legacy OAuth/refresh-token approach; current auth is Sanctum PAT-only with no device/subnet binding in code/config."} {"id":"fotospiel-app-e05","title":"SEC-MS-03 Checksum validation + alert thresholds","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:53:09.416197447+01:00","created_by":"soeren","updated_at":"2026-01-01T15:53:09.416197447+01:00"} {"id":"fotospiel-app-egu","title":"Tenant admin onboarding: welcome flow + routing guard + UI steps","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:07:48.767482545+01:00","created_by":"soeren","updated_at":"2026-01-01T16:07:54.375737724+01:00","closed_at":"2026-01-01T16:07:54.375737724+01:00","close_reason":"Completed in codebase (verified)"} +{"id":"fotospiel-app-exp","title":"Live Show: guest PWA submit-to-live toggle + copy","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-05T11:11:47.362988313+01:00","created_by":"soeren","updated_at":"2026-01-05T11:11:47.362988313+01:00","dependencies":[{"issue_id":"fotospiel-app-exp","depends_on_id":"fotospiel-app-t1k","type":"blocks","created_at":"2026-01-05T11:13:17.571273259+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-fp3","title":"Security review: guest PWA code audit (join tokens, uploads, SW cache)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:14.493336661+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:14.493336661+01:00"} {"id":"fotospiel-app-g5o","title":"SEC-MS-04 Storage health widget in Super Admin","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:53:15.088501536+01:00","created_by":"soeren","updated_at":"2026-01-01T15:53:20.739996548+01:00","closed_at":"2026-01-01T15:53:20.739996548+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-g74","title":"Paddle migration: automated tests for checkout/webhooks/sync","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:58:34.795423009+01:00","created_by":"soeren","updated_at":"2026-01-01T15:58:40.467997776+01:00","closed_at":"2026-01-01T15:58:40.467997776+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-gsv","title":"Localized SEO: validate hreflang via Search Console/Lighthouse","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:36.4821072+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:36.4821072+01:00"} +{"id":"fotospiel-app-h5d","title":"Live Show: decide display access model (token link vs kiosk session)","description":"# Decision: Display access model (public player view)\n\n## Context\nWe need a *hands-off* playback surface for projectors/TVs (“Live Show player view”). It must be easy to open on any device, safe to share with staff, and revocable if leaked.\n\nWe already use tokenized public routes for the guest PWA (`/g/{token}`, `/e/{token}/{path?}` in `routes/web.php`). We also use Laravel signed URLs for some one-off documents (gift voucher print), but that pattern is not ideal for long-running displays.\n\n## Options\n### A) Token-only show link (recommended)\n- Admin generates a **random, unguessable token** per event: e.g. `/show/{token}`.\n- Player view is publicly accessible *only* with the token.\n- Admin can **rotate** token to invalidate all current displays.\n- Optional: add **expires_at** for temporary links (off by default for convenience).\n\n**Pros**\n- Fastest operator workflow; no login needed on projector device.\n- Matches existing public-token patterns in this repo.\n- Simple to rotate/kill in case of leakage.\n\n**Cons**\n- Anyone with the link can view the show (mitigated by rotation and not showing PII).\n\n### B) Admin-authenticated “kiosk session”\n- Projector device logs in as an admin (or receives a kiosk session after admin auth) and then shows the player view.\n\n**Pros**\n- Strong access control.\n\n**Cons**\n- Operationally painful at venues (login, MFA, account management).\n- Higher support burden and more failure points.\n\n### C) Temporary signed URL (Laravel signed routes)\n- Generate a time-limited signed URL and use that for the player.\n\n**Pros**\n- No DB token storage required.\n\n**Cons**\n- Long-running displays need long expiry; link rotation/revocation is awkward.\n- Not great UX when a signature expires mid-event.\n\n## Recommended decision\nChoose **Option A: Token-only show link**.\n\n### Security posture\n- Token is treated as a secret: do not log full token; never display it in error messages.\n- Public player view must not display PII (no guest names, no device IDs).\n- Add rate limiting middleware for the show endpoints (lightweight protection against brute force).\n\n## Operational workflow\n1. Admin enables Live Show for an event.\n2. Admin copies the show link and opens it on the playback device.\n3. If link leaks: admin clicks **Rotate link** → all old links stop working.\n\n## Implementation notes (to guide downstream work)\n- Add `events.live_show_token` (random 32+ bytes, base64url/hex) and `events.live_show_token_rotated_at`.\n- New public route e.g. `GET /show/{token}` → serves the player SPA shell.\n- New public API endpoint resolved by token (read-only): `GET /api/v1/live-show/{token}/state` (settings + initial photo batch).\n- Realtime channel uses token to subscribe (or resolves event id server-side after token validation).\n\n## Decision needed from product\n- Confirm whether we want optional **expires_at** (default: no expiry, rely on rotation).\n- Confirm if multiple simultaneous display devices are supported (default: yes; token works naturally).\n","acceptance_criteria":"- Issue records options A/B/C with pros/cons\\n- Clear recommendation (token-only) with security considerations\\n- Concrete implementation notes (DB fields, route shape, revocation workflow)\\n- Explicit product decisions listed (expiry + multi-screen)","notes":"Decision: token-only show link per event; no expiry by default; rely on rotation/invalidation. Multi-screen supported naturally via token.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-05T11:43:11.636746837+01:00","created_by":"soeren","updated_at":"2026-01-05T12:06:45.909441986+01:00","closed_at":"2026-01-05T12:06:45.909441986+01:00","close_reason":"Closed","dependencies":[{"issue_id":"fotospiel-app-h5d","depends_on_id":"fotospiel-app-vro","type":"blocks","created_at":"2026-01-05T11:43:43.272652233+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-hbt","title":"Moderation queue for guest content","description":"Queue for flagged guest content (photos, feedback). Bulk actions to hide/delete/resolve with audit.","notes":"Land the plane: tests run (FilamentPanelNavigationTest, PhotoModerationQueueTest, TenantFeedbackModerationQueueTest, TenantLifecycle*), migrations added for photo + feedback moderation, no follow-up blockers.","status":"closed","priority":1,"issue_type":"feature","created_at":"2026-01-01T14:18:37.777772819+01:00","updated_at":"2026-01-02T17:33:22.599440896+01:00","closed_at":"2026-01-02T17:33:22.599440896+01:00","close_reason":"Closed"} {"id":"fotospiel-app-ihd","title":"Superadmin control surface spec and access matrix","description":"Define the minimal superadmin control surface, permissions, and mapping to tenant/guest responsibilities. Document scope and non-goals.","notes":"Added superadmin control surface + access matrix to docs/ops/operations-manual.md (Section 1.1), including non-goals and role scope.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T14:18:10.789147344+01:00","updated_at":"2026-01-02T17:33:57.71777777+01:00","closed_at":"2026-01-02T17:33:57.71777777+01:00","close_reason":"Closed"} {"id":"fotospiel-app-iqd","title":"Legal: disclose checkout/coupon fraud IP/device signals","description":"Update Legal Pages (privacy policy) to disclose IP/device capture for coupon fraud signals and retention window.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-02T23:33:44.532864199+01:00","created_by":"soeren","updated_at":"2026-01-04T11:15:55.947463643+01:00","closed_at":"2026-01-04T11:15:55.947463643+01:00","close_reason":"Closed"} @@ -96,9 +104,11 @@ {"id":"fotospiel-app-q2n","title":"Checkout refactor: wizard foundations + updated steps","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:58.701443698+01:00","created_by":"soeren","updated_at":"2026-01-01T16:06:04.313207281+01:00","closed_at":"2026-01-01T16:06:04.313207281+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-q4l","title":"Legal: disclose checkout/coupon fraud IP/device signals","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-02T23:33:09.347178363+01:00","created_by":"soeren","updated_at":"2026-01-02T23:33:54.68346278+01:00","closed_at":"2026-01-02T23:33:54.68346278+01:00","close_reason":"Closed"} {"id":"fotospiel-app-qlj","title":"Paddle catalog sync: verify legacy packages mapped before auto-sync","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:43.333792314+01:00","created_by":"soeren","updated_at":"2026-01-02T21:46:52.797515024+01:00","closed_at":"2026-01-02T21:46:52.797515024+01:00","close_reason":"Completed"} +{"id":"fotospiel-app-qne","title":"Live Show: realtime delivery channel (WS/SSE) + fallback polling","status":"open","priority":1,"issue_type":"feature","created_at":"2026-01-05T11:11:06.028871737+01:00","created_by":"soeren","updated_at":"2026-01-05T11:11:06.028871737+01:00","dependencies":[{"issue_id":"fotospiel-app-qne","depends_on_id":"fotospiel-app-t1k","type":"blocks","created_at":"2026-01-05T11:12:30.363982215+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-qtn","title":"Security review kickoff mitigations (CORS allowlist, headers, upload hardening, signed URLs)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:09:46.310873311+01:00","created_by":"soeren","updated_at":"2026-01-01T16:09:51.914359487+01:00","closed_at":"2026-01-01T16:09:51.914359487+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-sbs","title":"Compliance tools: data export + retention overrides","description":"GDPR-compliant export requests and retention override workflows for tenants/events.","status":"closed","priority":3,"issue_type":"feature","created_at":"2026-01-01T14:20:16.530289009+01:00","updated_at":"2026-01-02T20:13:31.704875591+01:00","closed_at":"2026-01-02T20:13:31.704875591+01:00","close_reason":"Closed"} {"id":"fotospiel-app-swb","title":"Security review: replace public asset URLs with signed routes","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:04:05.610098299+01:00","created_by":"soeren","updated_at":"2026-01-01T16:04:11.215921463+01:00","closed_at":"2026-01-01T16:04:11.215921463+01:00","close_reason":"Completed in codebase (verified)"} +{"id":"fotospiel-app-t1k","title":"Live Show: data model \u0026 status workflow (pending/approved/ready)","acceptance_criteria":"- DB migrations add event token + photo live fields + indexes\\n- Token generation supports rotation (no expiry)\\n- Photo live workflow methods set timestamps/reviewer consistently\\n- Feature test covers token + workflow","notes":"Implemented Live Show data model: events.live_show_token + live_show_token_rotated_at; photos.live_status + timestamps/reviewer/rejection fields + indexes. Added PhotoLiveStatus enum and Photo workflow methods (markLivePending/approveForLiveShow/rejectForLiveShow). Added Event helpers (ensureLiveShowToken/rotateLiveShowToken). Tests: tests/Feature/LiveShowDataModelTest.php.","status":"closed","priority":1,"issue_type":"feature","created_at":"2026-01-05T11:10:56.560421826+01:00","created_by":"soeren","updated_at":"2026-01-05T12:22:51.967913423+01:00","closed_at":"2026-01-05T12:22:51.967913423+01:00","close_reason":"Closed","dependencies":[{"issue_id":"fotospiel-app-t1k","depends_on_id":"fotospiel-app-vro","type":"blocks","created_at":"2026-01-05T11:12:20.345646244+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-t1k","depends_on_id":"fotospiel-app-h5d","type":"blocks","created_at":"2026-01-05T11:44:12.439413712+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-t1k","depends_on_id":"fotospiel-app-1eu","type":"blocks","created_at":"2026-01-05T11:44:22.588642567+01:00","created_by":"soeren"},{"issue_id":"fotospiel-app-t1k","depends_on_id":"fotospiel-app-1we","type":"blocks","created_at":"2026-01-05T11:44:31.775634827+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-tqg","title":"Tenant admin onboarding: staging E2E validation","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:08:57.448899354+01:00","created_by":"soeren","updated_at":"2026-01-01T16:08:57.448899354+01:00"} {"id":"fotospiel-app-ty9","title":"Security review: data classes \u0026 retention baseline","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:03:09.595870306+01:00","created_by":"soeren","updated_at":"2026-01-01T16:03:15.211042718+01:00","closed_at":"2026-01-01T16:03:15.211042718+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-tym","title":"Ops health dashboard (queues, storage, upload pipeline)","description":"Superadmin ops dashboard showing queue backlog, failed jobs, storage thresholds, and upload pipeline health.","notes":"Implemented Ops Health dashboard with storage+queue widgets, new translations, and navigation wiring.","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-01-01T14:20:04.991351193+01:00","updated_at":"2026-01-02T17:34:10.326367902+01:00","closed_at":"2026-01-02T17:34:10.326367902+01:00","close_reason":"Closed"} @@ -110,11 +120,13 @@ {"id":"fotospiel-app-vc3","title":"Localized SEO: add tests for hreflang/canonical tags","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:30.822184391+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:30.822184391+01:00"} {"id":"fotospiel-app-vel","title":"Localized SEO: update PRP/marketing playbooks for hreflang strategy","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:42.156443813+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:42.156443813+01:00"} {"id":"fotospiel-app-vk4","title":"Registration flow fixes: JSON redirect, error clearing, role handling","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:07:01.574904029+01:00","created_by":"soeren","updated_at":"2026-01-01T16:11:18.65499639+01:00","closed_at":"2026-01-01T16:11:18.65499639+01:00","close_reason":"Duplicate of fotospiel-app-l6a"} +{"id":"fotospiel-app-vro","title":"Live Show: product spec \u0026 UX flow","description":"# Live Show — Full Spec (including edge cases \u0026 moderation)\n\n## Goal\nProvide a “Live-Slideshow ready” mode that shows newly uploaded event photos on any screen (projector / TV / LED wall) within seconds, with stunning but tasteful effects and strong operational controls.\n\n## Target outcomes\n- **Delight**: looks premium on large screens; smooth transitions; branding-consistent.\n- **Speed**: new uploads appear quickly (target: \u003c5s after upload completes, subject to moderation rules).\n- **Control**: event staff can moderate, pause, and tune effects/tempo live.\n- **Resilience**: continues gracefully under network hiccups and high upload volume.\n\n## User roles\n- **Guest (Guest PWA)**: uploads photos; optionally opts into “show on Live Show”.\n- **Moderator (Admin app)**: approves/rejects/holds photos; can do quick actions.\n- **Event Operator (Admin app)**: configures show, effects, and playback behavior.\n- **Display Device (public player view)**: full-screen playback; should be hands-off.\n\n## Surfaces (UX)\n1) **Guest PWA**\n - Post-upload confirmation includes a toggle: “Auf Live-Show zeigen” (default behavior is configurable per event).\n - If moderation is enabled: copy clarifies “wird nach Freigabe gezeigt”.\n\n2) **Admin app: Live Show Control**\n - Start/stop show, generate/rotate show link (token), screen preview.\n - Live settings: effect preset, intensity, tempo, layout, branding.\n - Operational controls: pause, skip, emergency “safe mode”, clear queue.\n\n3) **Admin app: Moderation Queue**\n - Inbox of newest photos that are eligible for Live Show but not yet approved.\n - Fast actions: Approve / Reject / Hold.\n - Optional assignment + SLA hints (e.g., “pending \u003e 2min”).\n\n4) **Public player view (Show Link)**\n - Full-screen slideshow with optional subtle UI chrome (small “Live” badge, event name, logo).\n - On reconnect/offline: non-intrusive overlay (“Verbindung… zeige zuletzt geladene Fotos”).\n\n## Data \u0026 states\n### Photo lifecycle (Live Show)\nWe need a clear status machine for each photo relative to Live Show.\n\n**Proposed enum** `live_status`:\n- `none` (not participating)\n- `pending` (submitted / eligible but awaiting moderation)\n- `approved` (eligible for playback)\n- `rejected` (excluded; optionally with reason)\n- `expired` (no longer in rotation due to policy or time window)\n\n**Fields (minimum)**\n- `live_status`\n- `live_submitted_at`\n- `live_reviewed_at`\n- `live_reviewed_by` (admin user id)\n- `live_rejection_reason` (short code; no PII)\n- `live_priority` (optional; “featured”)\n\n### Queue model\nTwo acceptable approaches:\n- **Derived queue**: query `photos where live_status=approved` ordered by `live_submitted_at` (and other ranking signals). Player maintains an in-memory queue.\n- **Explicit queue table**: store ordered entries with `position`, `state` (queued/played/skipped). Better for deterministic playback and analytics.\n\nStart with derived queue unless we need strict deterministic ordering across multiple display devices.\n\n### Live Show settings (per event)\n- `enabled` (bool)\n- `moderation_mode` (enum): `off`, `manual`, `trusted_only`\n- `trusted_rules` (who counts as trusted; e.g., staff devices, whitelisted invites)\n- `playback_mode` (enum): `newest_first`, `balanced`, `curated`\n- `freshness_bias` (0..1)\n- `pace_mode` (enum): `auto`, `fixed`\n- `fixed_interval_seconds` (e.g., 6–12)\n- `layout_mode` (enum): `single`, `split`, `grid_burst`\n- `effect_preset` (enum)\n- `effect_intensity` (0..100)\n- `background_mode` (enum): `blur_last`, `gradient`, `solid`, `brand`\n- `branding` (logo, accent color, typography choice)\n- `content_rules` (e.g., exclude flagged/reported content)\n- `retention_window_hours` (how long photos remain eligible for show)\n\n## Realtime behavior\n### Event types\n- `photo.live_submitted` (guest opted in)\n- `photo.live_pending` (needs review)\n- `photo.live_approved`\n- `photo.live_rejected`\n- `live_show.settings_updated`\n\n### Delivery\n- Prefer **WebSockets** for low-latency updates.\n- Provide **fallback polling** (5–10s) for environments where WS is blocked.\n- Display device should handle:\n - reconnect with exponential backoff\n - resync on reconnect: fetch latest approved list and settings\n\n### Consistency rules\n- Player should de-duplicate by photo id.\n- If an already-playing photo is later rejected, it must be removed from future rotation (don’t abruptly cut current frame unless “safe mode” is triggered).\n\n## Playback \u0026 effects engine\n### Visual goals\n- “Stunning” but not distracting. Effects should be smooth and GPU-friendly.\n- Provide a “Light Effects” preset for weaker hardware.\n\n### Presets (initial)\n- **Film Cut**: clean fades + slight grain overlay\n- **Shutter Flash**: quick flash + slide-in\n- **Polaroid Toss**: photo card with shadow + slight rotation settle\n- **Parallax Glide**: slow zoom/pan (Ken Burns-like) with depth blur background\n- **Light Effects**: minimal transitions, no heavy overlays\n\n### Layout modes\n- `single`: full-screen hero image (default)\n- `split`: two photos alternating/paired (only if aspect ratios allow)\n- `grid_burst`: quick mosaic of 4–9 images, then one becomes hero\n\n### Performance constraints\n- Always use optimized image sizes; preload next image.\n- Use placeholder (blurhash/LQIP) to avoid white flashes.\n- Cap memory: keep only N decoded images in memory (e.g., 3–5).\n\n## Moderation rules\n### Modes\n1) **Moderation Off**\n - All submitted photos go directly to `approved`.\n - Still exclude photos that are flagged/reported.\n\n2) **Manual Moderation**\n - Default: `pending` until staff approves.\n - SLA guidance: “pending \u003e 2min” highlight.\n\n3) **Trusted-only auto-approve**\n - If uploader matches trusted rules: `approved`, else `pending`.\n\n### Actions\n- **Approve**: sets `approved`, emits realtime event.\n- **Reject**: sets `rejected` + reason code, emits event.\n- **Hold**: stays `pending` (optionally substate `held`).\n\n### Edge cases\n- High-volume: moderation queue batching (“approve next 10”).\n- Safety: “Safe Mode” instantly pauses new arrivals and switches to curated backlog.\n- Disputes: allow reversing a decision (approve previously rejected) with audit trail.\n\n## Security \u0026 access control\n- Show link should be **tokenized** per event (unguessable), optionally with expiry.\n- Allow **rotate token** (invalidate old displays).\n- No PII on screen by default (no guest names, no device identifiers).\n- Avoid introducing new tracking beyond existing anonymous session id policies.\n\n## Failure modes \u0026 graceful degradation\n- **No network**: keep showing last cached set; show subtle “offline” overlay.\n- **Slow network**: delay effects until image is decoded; avoid stutter.\n- **Blocked WebSockets**: auto-fallback to polling.\n- **Projector handshake/resolution change**: player should reflow and continue.\n- **Bad image/corrupt**: skip and mark as failed-to-render (internal), never block queue.\n- **Time drift**: do not depend on exact wall-clock sync.\n\n## Analytics (optional, privacy-safe)\n- Aggregate only: photos shown count, average time-to-screen, moderation throughput.\n- Do not log uploader identity or precise device fingerprints.\n\n## Out of scope (hard boundaries)\n- Videos.\n- Facial recognition.\n- Public profiles.\n- Any new user tracking beyond documented anonymous session_id.\n\n## Open questions (need decisions)\n- Should the show link be public token-only, or require admin login with a “kiosk session”?\n- Do we need deterministic playback ordering across multiple screens (explicit queue table), or is “eventually consistent” OK?\n- Trusted rules definition: staff-only, invite whitelist, or per-device pairing?\n- Maximum on-screen retention window default (e.g., 4h vs full event)?\n","acceptance_criteria":"- Live Show spec includes: surfaces, roles, states, realtime, playback/effects, moderation, security, failure modes\\n- Explicit open questions listed for product decisions\\n- Spec is implementation-ready and can directly feed downstream issues","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-05T11:10:47.029662438+01:00","created_by":"soeren","updated_at":"2026-01-05T11:44:51.675995334+01:00","closed_at":"2026-01-05T11:44:51.675995334+01:00","close_reason":"Closed"} {"id":"fotospiel-app-w2x","title":"SEC-FE-03 Cookie banner UX + localisation","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:55:26.182193434+01:00","created_by":"soeren","updated_at":"2026-01-01T15:55:31.84344419+01:00","closed_at":"2026-01-01T15:55:31.84344419+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-w7g","title":"Paddle catalog sync: document failure recovery playbook","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:26.255623751+01:00","created_by":"soeren","updated_at":"2026-01-02T21:52:16.074002452+01:00","closed_at":"2026-01-02T21:52:16.074002452+01:00","close_reason":"Completed"} {"id":"fotospiel-app-wde","title":"Tenant lifecycle controls (status, limits, suspend/grace)","description":"Superadmin controls for tenant status, grace periods, and hard limits (uploads/storage/events). Includes UI, policy checks, and audit events.","notes":"Delivered dedicated tenant lifecycle view with limits + audit timeline, added grace_period_ends_at field and tenant_lifecycle_events logging, wired lifecycle actions (activate/suspend/deletion/anonymize) + management actions (limits, grace, subscription expiry), enforced tenant photo/storage limits in PackageLimitEvaluator, added lifecycle/limits tests, ran Pint + targeted tests.","status":"closed","priority":1,"issue_type":"feature","created_at":"2026-01-01T14:18:23.062036821+01:00","updated_at":"2026-01-02T17:33:35.031605632+01:00","closed_at":"2026-01-02T17:33:35.031605632+01:00","close_reason":"Closed"} {"id":"fotospiel-app-wkl","title":"Paddle catalog sync: paddle:sync-packages command (dry-run/pull)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:58.753792575+01:00","created_by":"soeren","updated_at":"2026-01-01T16:01:04.39629062+01:00","closed_at":"2026-01-01T16:01:04.39629062+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-wku","title":"Security review: run dynamic testing harness (identities, DAST, fuzz uploads)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:37.008239379+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:37.008239379+01:00"} +{"id":"fotospiel-app-xg5","title":"Live Show: Admin app moderation queue UI","status":"open","priority":2,"issue_type":"feature","created_at":"2026-01-05T11:11:15.006484132+01:00","created_by":"soeren","updated_at":"2026-01-05T11:11:15.006484132+01:00","dependencies":[{"issue_id":"fotospiel-app-xg5","depends_on_id":"fotospiel-app-t1k","type":"blocks","created_at":"2026-01-05T11:12:38.94145573+01:00","created_by":"soeren"}]} {"id":"fotospiel-app-xht","title":"Paddle migration: tenant ↔ Paddle customer sync + webhook handlers","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:58:01.028435913+01:00","created_by":"soeren","updated_at":"2026-01-01T15:58:06.685122343+01:00","closed_at":"2026-01-01T15:58:06.685122343+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-y1f","title":"Compliance tools: superadmin data export + retention override UI","description":"Add superadmin compliance tools for data exports and retention overrides.\nScope: list export requests, status, expiry, and allow manual retry/cancel; add per-tenant/event retention override UI with audit logging.\nEnsure access is restricted to superadmins and no PII is exposed beyond existing export metadata.\n","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-02T17:34:29.825347299+01:00","created_by":"soeren","updated_at":"2026-01-02T22:49:53.586758621+01:00","closed_at":"2026-01-02T22:49:53.586758621+01:00","close_reason":"Closed"} {"id":"fotospiel-app-z2k","title":"Ops health widget visual polish","description":"Replace Tailwind utility styling in ops health widget with Filament components and icon-driven layout.","notes":"Updated queue health widget layout to use Filament cards, badges, empty states, and grid utilities; added status strip and alert rail.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-01T21:34:39.851728527+01:00","created_by":"soeren","updated_at":"2026-01-01T21:34:59.834597413+01:00","closed_at":"2026-01-01T21:34:59.834597413+01:00","close_reason":"completed"}