Skip to main content
Petanque Life
← Back to all features
15

Mobile & Fan Experience

168 features · 14 subsystems

Mobile applications, spectator/fan features, gamification, and community engagement tools.

Mobile Application

F15.01
Shipped
How it works
  • F15.01.01 Shipped

    Cross-platform app (iOS, Android, Web) (React Native/Expo)

    ✅ PL-F1501a
  • F15.01.02 Shipped

    Player mobile app (profile, competitions, results, rankings)

    ✅ PL-F1501a
  • F15.01.03 Shipped

    Official mobile app (match management, score entry)

    ✅ PL-F1501a
  • F15.01.04 Shipped

    Club admin mobile app (member management, communication)

    ✅ PL-F1501a
  • F15.01.05 Shipped

    Spectator mobile app (live scores, schedule, venue info)

    ✅ PL-F1501a
  • F15.01.06 Shipped

    Offline mode (score entry without connectivity, sync later)

    ✅ PL-F1501b
  • F15.01.07 Shipped

    Push notifications

    ✅ PL-F1501b · Go-ombyggnad: transporten LEVERERAD i 4776ad23.m-1 (D-PUSH) — POST /v1/push/devices + POST /v1/push/dispatch (202-jobbväg för bred fan-out) mot APNs/FCM v1. app-sidan LEVERERAD i 4776ad23.m-3 (task 4776ad23.m-3.t-2): kontextuell behörighets-onboarding efter genomförd anmälan (aldrig vid kallstart), registrering med råa APNs-/FCM-token (getDevicePushTokenAsync), token-rotation och avregistrering vid logout före sessionsrivningen, skärmen Notiser & enheter (/notiser — maskerad token, avregistrering med bekräftelse) med permanent ingång i Inställningar, foreground-/kallstartsmottagning, fast kategorikatalog (packages/shared/src/push/categories.ts, inkl. Godkänn/Avslå), säker deep-link-navigering (endast interna routes, fallback Hem) och badge-nollställning vid appöppning — vy-spec specs/app/views/notiser-enheter.md, bevisad av L4-drivrutinen tools/gate/dpush-m3-app-e2e.mjs mot körande stack + riktig Postgres. Ärlig gräns (A-3/A-5): riktig OS-dialog, bakgrundsrendering och åtgärdsknappar på fysisk enhet verifieras först i molnet med skarpa APNs-/FCM-nycklar, och notis-preferenser/tysta tider/sammanfattningar ägs av D-COMM (ej byggda — vyn säger det rakt ut, ingen död knapp)
  • F15.01.08 Shipped

    QR code scanner (license verification, check-in)

    ✅ PL-F1501b · Go-ombyggnad: LEVERERAD i 3e1559c7.m-3 (D-COURTSIDE) — appens /pa-banan?verktyg=qr KOMPONERAR den befintliga anonyma licensverifieringen publicVerifyLicense (fyra utfall, tre kort) och tävlingsdisken /offline-incheckning (bakom registrations:write) med husets ENDA skanneradapter (QrCaptureView) och grammatik (parseScannedCheckInPayload); klassificeringen är den rena classifyScannedPayload i packages/shared/src/courtside. Ingen POST /mobile/qr/verify, ingen ny endpoint; spelarens kortlivade incheckningstoken känns igen men konsumeras inte (⊘ med ägare D-USERS/D-EVENTS). Domänregel: docs/domain/courtside/README.md §QR-kompositionen, BR33.09
  • F15.01.09 Shipped

    GPS-based nearby venue/competition finder — NY HEMVIST: D-DISC GET /v1/discover/nearby (anonym cross-tenant radie-sök, PostGIS ST_DWithin mot GiST-indexet; radie default 10 km och tak 39 km härlett ur BR26.03:s areabudget, cap 100 med truncated, ordning på distance_km med en decimal, CDN-cache 300 s). Varje rad bär sina nästa tre drop-in-pass ur m-2:s enda expansion i platsens egen zon, och competitions-halvan är ett objekt med ett mätt källtillstånd (measured

    unavailable) ur D-COMP:s egen publika upptäcktssöm — aldrig en tom lista som ser ut att betyda »inga tävlingar«. Kontrakt: specs/api/endpoints/discover-nearby.md | ✅ PL-F1501b (legacy) · ✅ D-DISC 9e7fb823.m-3
  • F15.01.10 Shipped

    Dark mode and accessibility settings

    ✅ PL-F1501b
  • F15.01.11 Planned

    Uppdatering över luft (OTA) i butiksappen — D-MOBILDIST c784b7d5.m-1 (skalet): expo-updates installerat och wirat i app; runtimeVersion följer fingerprint-policyn och adressen bär kanal/plattform/butiksversion (<PL_UPDATES_BASE_URL>/<kanal>/<ios\

    android>/<version>/manifest.json, husets egen domän updates.test.petanque.life/updates.petanque.life), härledd per byggprofil i app/app.config.ts av den rena regeln app/updates/resolve-updates-config.ts (en native-profil med saknat värde stoppar bygget med variabelns namn); signerat från dag ett (publikt certifikat per miljö i app/updates/certs/, keyid pl-updates-<env>-v1, privata nycklar i driftvalvet); kontrollen ligger aldrig i startvägen (checkAutomatically: NEVER, fallbackToCacheTimeout: 0) — app/lib/updates/runtime.tsx hämtar i bakgrunden 15 s efter sessionsproben och vid återkomst till förgrunden (högst var 6:e timme), aldrig under ett arbetspass (scoring, runda, incheckning, studio, betalning, eller medan offline-kön synkar), och den hämtade bundlen börjar gälla först vid nästa kallstart (appen startar aldrig om sig själv). Expo Web-vägen är orörd. D-MOBILDIST c784b7d5.m-2 (publiceringsvägen): tools/mobile-updates/pl-updates.mjs publicerar signerade bundles som statiska filer på statichost.eu (pl-updates-test, updates.test.petanque.life) med expo-signature i en genererad _headers, vägrar med skalets egen bindningsregel (fingerprint, kanal, plattform), binder varje butiksversion till en fingerprint och en nyckel, återgår i en drop (mätt under 60 s), gallrar med --keep och roterar/retirerar nycklar (keyid ur certets CN); driftrutinen, ägarcheckistan och bevisprotokollet står i docs/engineering/runbooks/mobil-uppdateringar.md. D-MOBILDIST c784b7d5.m-3 (intern distribution, steg A): ägarens verktyg tools/mobile-dist/pl-dist.mjs (preflight → build → record-build → submit, status) bygger beta-profilen med EAS, bokför varje färdigt bygge i liggaren med EAS-byggets eget runtimeVersion innan det får lämnas in (första fingerprint-bindningen är inte längre tyst), lämnar in Android före iOS till Play internal testing och TestFlight (interna testare) och vägrar production; statusvyn /uppdateringar visar byggets identitet (app-uppdateringar-bygge); runbookens §4/§4a/§6 beskriver proceduren, testarkohorten och B-7/B-8. D-MOBILDIST c784b7d5.m-4 (steg B, butiksfärdig — inte publicerad): släppregistret app/store/ (listningar i de fem lanseringsspråken, integritetsdeklarationen härledd ur koden och prövad i båda riktningarna, åldersgränsens frågesvar, granskningsanteckningar, skärmbildsplan, release.json) renderas av pl-dist.mjs store render till privacy manifest/locales/android.permissions i app.json, EAS Metadata och Play-checklistorna; www bär app-policyn och sidan för kontoradering; production kräver --confirm-production och en öppnad släppgrind i liggaren (release-gate open), Android lämnas in först med stegvis utrullning. Kvar: Partial tills app/store/release.json store_urls bär butiksadresser — konsolformulären, granskningarna och publiceringen är ägarens (runbook §4 *Steg B*). Bevisets gräns: konton, ett riktigt EAS-bygge, installation via spåren, den riktiga värdens _headers, signaturverifiering på enhet och aktivering vid kallstart bevisas manuellt enligt bevisprotokollet (m-2/m-3) — ännu ej kört. D-UPPDATERANU 18248906.m-1 (»En ny version finns«): när en bundle är hämtad visar appen notisen med »Starta om nu« (aktiverar genom reloadAsync bakom den delade regeln activationDecision, aldrig under ett arbetspass) och »Senare« (döljer notisen för körningen; bundlen tas ändå in vid nästa kallstart); samma knapp i /uppdateringar — vy-spec specs/app/views/uppdateringsnotis.md. | 🟡 D-MOBILDIST c784b7d5.m-1 + c784b7d5.m-2 + c784b7d5.m-3 + c784b7d5.m-4
  • F15.01.12 Shipped

    Arrangörsappen — den ANDRA butiksappen (ägarbeslut 2026-09-18: *»många har inte dator utan bara telefon«*), arrangor/ (*Petanque Life Arrangör*, life.petanque.arrangor). Byggd som KOMPOSITION av packages/shared, aldrig som kopia av app/: native bearer-session i expo-secure-store (Expo Web cookie i bevisläge), OTP-/provider-/dev-login-inloggning, tenantval, det delade adaptiva skalet med nav ur admins EGEN resolveAdminNav(caps), capability-grind fail-closed med neutral nekande-yta, hem med gruppkort och klientfel (surface: 'arrangor') från första bygget. D-MOBILDIST ärvd med egna cert (pl-arrangor-updates-<env>-v1), egen manifestadress och egen pl-dist-liggare. Vyerna flyttas i vågor ur admin (fältytorna först). Vy-spec: specs/arrangor/views/skal-och-inloggning.md. D-UPPDATERANU 18248906.m-1: samma notis *»En ny version finns«* med »Starta om nu«/»Senare« som spelarappen, aldrig på arbetspass-rutterna (ARRANGOR_WORK_PASS_ROUTES); reloadAsync bor i exakt arrangor/lib/updates/runtime.ts.

    ✅ D-ADMINAPP 94f74ebf.m-1.t-2 · D-UPPDATERANU 18248906.m-1

Live Match Experience

F15.02
Planned
How it works
  • F15.02.01 Shipped

    Real-time live scores across all courts

    ✅ PL-F1502a
  • F15.02.02 Shipped

    Match timeline (point-by-point history)

    ✅ PL-F1502a
  • F15.02.03 Shipped

    Court view (visual representation of ongoing matches)

    ✅ PL-F1502a
  • F15.02.04 Shipped

    Follow specific player/team (personalized updates)

    ✅ PL-F1502a
  • F15.02.05 Shipped

    Live commentary feed

    ✅ PL-F1502b
  • F15.02.06 Shipped

    Score alerts and notifications

    ✅ PL-F1502b
  • F15.02.07 Shipped

    Multi-match view (watch multiple courts)

    ✅ PL-F1502b
  • F15.02.30 Shipped

    Adaptive bitrate stream profile (HLS/DASH renditions, server-managed)

    ✅ PL-T219
  • F15.02.31 Shipped

    Picture-in-picture support flag per stream profile

    ✅ PL-T219
  • F15.02.32 Shipped

    Commentary language toggle (Accept-Language sortering, default-track)

    ✅ PL-T219
  • F15.02.33 Shipped

    Stream geo-block policy (ISO-3166-1 alpha-2, expiry, requires_auth)

    ✅ PL-T219

Community Features

F15.03
Planned
How it works
  • F15.03.01 Shipped

    Player profile pages (public)

    ✅ PL-F1503a
  • F15.03.02 Shipped

    Club profile pages (public)

    ✅ PL-F1503a
  • F15.03.03 Shipped

    Follow players and clubs

    ✅ PL-F1503a
  • F15.03.04 Shipped

    Activity feed (followed players' results, upcoming matches)

    ✅ PL-F1503a
  • F15.03.05 Shipped

    Find-a-player (looking for team members)

    ✅ PL-F1503b
  • F15.03.06 Shipped

    Find-a-club (new player looking for local club)

    ✅ PL-F1503b
  • F15.03.07 Shipped

    Forums / discussion boards per club, region, topic

    ✅ PL-F1503b
  • F15.03.08 Shipped

    Event reviews and photos sharing

    ✅ PL-F1503b
  • F15.03.20 Shipped

    Multilingual forums (locale-default + i18n_strings, BCP-47-validering)

    ✅ PL-T219
  • F15.03.21 Shipped

    Cross-locale forum discovery (locale=* aggregerar alla språk)

    ✅ PL-T219
  • F15.03.22 Shipped

    Forum auto-moderation (regel-baserad: profanity-regex, link-spam, all-caps, spam-score) — rebuilden: 0154301b.m-6 (bedömaren internal/social/moderation, håller inlägg/kommentarer vid spam_score ≥ 0.70 eller svordom, flaggar men håller aldrig meddelanden)

    ✅ PL-T219
  • F15.03.23 Shipped

    Forum mentions + reactions (@user → push-pipeline, emoji-toggle på posts)

    ✅ PL-T219

Gamification

F15.04
Planned

**Greenfield på `rebuild` — D-GAME `efec20d6.m-1` (progressionskärnan) och `efec20d6.m-2` (utmaningar och topplistor).** Raderna ovan beskriver Mongo-erans yta och byggs inte om. På `rebuild` är följande levererat av `efec20d6.m-1` (API-delen i t-1, appytan `/utmaningar` i t-2):

How it works
  • F15.04.01 Shipped

    Achievement badges (first competition, 100 matches, etc.) — PL-F1504a

    ✅ PL-F1504a
  • F15.04.02 Shipped

    Milestone tracking and celebrations — PL-F1504a

    ✅ PL-F1504a
  • F15.04.03 Shipped

    Prediction game (predict competition outcomes) — PL-F1504a

    ✅ PL-F1504a
  • F15.04.04 Shipped

    Fantasy petanque (build dream team, earn points) — PL-F1504a

    ✅ PL-F1504a
  • F15.04.05 Shipped

    Leaderboards (most active player, most competitions, etc.) — PL-F1504a

    ✅ PL-F1504a
  • F15.04.06 Shipped

    Challenge system (challenge another player to a match) — PL-F1504b

    ✅ PL-F1504b
  • F15.04.07 Shipped

    Streak tracking (consecutive wins, active weeks) — PL-F1504b

    ✅ PL-F1504b
  • F15.04.08 Shipped

    Daily/weekly challenges (community-wide skill challenges) — PL-F1504b

    ✅ PL-F1504b
  • F15.04.09 Shipped

    Club challenges (club vs club in weekly mini-competitions) — PL-F1504b

    ✅ PL-F1504b
  • F15.04.10 Shipped

    Seasonal quests (play X matches, visit Y venues, enter Z tournaments) — PL-F1504b

    ✅ PL-F1504b
  • F15.04.11 Shipped

    XP and level system (based on activity, not just wins)

    ✅ PL-F1504c
  • F15.04.12 Shipped

    Personal bests tracking (best ELO, best tournament result, longest streak)

    ✅ PL-F1504c
  • F15.04.13 Shipped

    Fan predictions for live matches (predict score, earn points)

    ✅ PL-F1504c
  • F15.04.14 Shipped

    Pick'em brackets (predict entire tournament outcomes)

    ✅ PL-F1504c
  • F15.04.15 Shipped

    Celebration UX motor — proportional confetti / banner / badge primitives, registry-governed events, max-1-major-per-session rate limit, audit gate

    ✅ PL-T163
  • F15.04.16 Shipped

    Reduced-motion + background-safe celebration playback (web prefers-reduced-motion + native AppState)

    ✅ PL-T163
  • F15.04.17 Shipped

    Personal-best comparison primitive with stagger animation

    ✅ PL-T163

Accessibility & Inclusion

F15.05
Planned
How it works
  • F15.05.01 Shipped

    Multi-language interface (French, English, Spanish, + federation languages)

    ✅ PL-F1505a, ♻️ Rebuild fa798ce7.m-4.t-2 (D-LOCALE FR-2: appens språkkedja är WIRAD — I18nProvider i app/app/_layout.tsx med resolvad locale + runtime-bundles, upplösning kontots locale → enhetsspråk (expo-localization / navigator.languages) → aktiva tenantens default_locale → en, NAV-etiketterna ur microcopy (appNav.*) i stället för svenska literaler, och språkfältet i Inställningar (app-language-picker) som PATCH:ar kontot (PATCH /v1/me) och byter UI:t utan omladdning. Valet följer kontot till web utan delade cookies; utloggad är fältet ärligt avstängt; offline/404 degraderar tyst till de statiska bundlarna. Se specs/app/views/installningar.md)
  • F15.05.02 Shipped

    Screen reader compatibility (WCAG 2.1 AA)

    ✅ PL-F1505a
  • F15.05.03 Shipped

    High contrast mode

    ✅ PL-F1505a
  • F15.05.04 Shipped

    Font size adjustment

    ✅ PL-F1505a
  • F15.05.05 Shipped

    Simplified interface mode (for non-technical users)

    ✅ PL-F1505b
  • F15.05.06 Shipped

    Age-appropriate interfaces (youth vs. adult)

    ✅ PL-F1505b
  • F15.05.07 Shipped

    Right-to-left language support

    ✅ PL-F1505b

Onboarding & Discovery

F15.06
Planned
How it works
  • F15.06.01 Shipped

    New player onboarding wizard — PL-F1506a

    ✅ PL-F1506a, ✅ PL-T308 (premium editorial multi-step flow: animerad welcome-sekvens, swipe-stack mobil + split-screen desktop med crossfade <StepIllustration>, persistent draft i AsyncStorage, tenant-aware licens-katalog, konfetti-celebration via shared useCelebrate('onboarding.complete') med reduced-motion-glow, ProgressDots med accessibilityRole="progressbar", focus-shift per steg-shift, skip-affordance på notifikations- och preferenssteg)
  • F15.06.02 Shipped

    "Getting started with petanque" content — PL-F1506a

    ✅ PL-F1506a
  • F15.06.03 Shipped

    Find your nearest club (location-based) — PL-F1506a. WEB-HEMVISTEN: D-DISC 9e7fb823.m-4 — den publika, oautentiserade cross-tenanta findern web /hitta-spel (SSR + klient-ö, radien som en SLUTEN mängd {2, 5, 10, 25, 39} km, varje filter en SERVERFRÅGA, kartpanel med RIKTIGA markörer och bakgrundslagret default AV, de fem tillstånden där *ingen position* aldrig är *tomt*, tiden i spelplatsens zon, ingenting lagrat i webbläsaren). Varje rad kommer ur GET /v1/discover/nearby — ortsökningen är ⊘ med ägare F14.11 och mockupens attrappdata byggs aldrig. Den enskilda platsens SEO-yta /spelplatser/{slug} är LEVERERAD i 9e7fb823.m-4.t-2 (⊘:t HÄVT, inte klippt): profil, publik tillgänglighetsvy, drop-in i platsens tid med undantagen som egna rader, serverns actions[], länkgrafen *Andra spelplatser i närheten* (m-4:s ersättning för en uppräknande sitemap) och en opak 404 som varken ekar slugen eller skiljer en okänd plats från en opublicerad. Design-kontraktsraden: design-contract.md

    ✅ PL-F1506a (app) · ✅ D-DISC 9e7fb823.m-4 (web)
  • F15.06.04 Shipped

    Try petanque events listing (beginner-friendly) — PL-F1506a

    ✅ PL-F1506a
  • F15.06.05 Shipped

    Rules overview for newcomers — PL-F1506b

    ✅ PL-F1506b
  • F15.06.06 Shipped

    Equipment guide for beginners — PL-F1506b

    ✅ PL-F1506b
  • F15.06.07 Shipped

    Sport ambassador program — PL-F1506b

    ✅ PL-F1506b
  • F15.06.10 Shipped

    Public club discovery map — NY HEMVIST: D-DISC GET /v1/discover/map (PostGIS/GiST, anonym cross-tenant bbox-sök, 5 000 km²-cap, 500-resultat-cap + truncated, antimeridian-split med dedup, CDN-cache 300 s). Kontrakt: specs/api/endpoints/discover-map.md; legacy-formen /public/club-profiles/map är referens, ej kontrakt. Innehållet projiceras av svepet discovery_index_reconcile ur D-VENUE/D-ORGS. Den enskilda platsens publika profil (namn, ytor, nästa pass, tillgänglighetsvy, serverburna djuplänkar och färskhetsmärket) är GET /v1/discover/places/{slug} — specs/api/endpoints/discover-places.md, D-DISC 9e7fb823.m-3; färskheten (fresh

    stale, BR26.05) är en HÄRLEDNING vid läsning och bärs på profilen och nearby, medvetet inte på kartan (beslut D-12). WEB-HEMVISTEN: D-DISC 9e7fb823.m-4 — den publika, oautentiserade cross-tenanta findern web /hitta-spel (SSR + klient-ö, radien som en SLUTEN mängd {2, 5, 10, 25, 39} km, varje filter en SERVERFRÅGA, kartpanel med RIKTIGA markörer och bakgrundslagret default AV, de fem tillstånden där *ingen position* aldrig är *tomt*, tiden i spelplatsens zon, ingenting lagrat i webbläsaren). Varje rad kommer ur GET /v1/discover/nearby — ortsökningen är ⊘ med ägare F14.11 och mockupens attrappdata byggs aldrig. Den enskilda platsens SEO-yta /spelplatser/{slug} är LEVERERAD i 9e7fb823.m-4.t-2 (⊘:t HÄVT, inte klippt): profil, publik tillgänglighetsvy, drop-in i platsens tid med undantagen som egna rader, serverns actions[], länkgrafen *Andra spelplatser i närheten* (m-4:s ersättning för en uppräknande sitemap) och en opak 404 som varken ekar slugen eller skiljer en okänd plats från en opublicerad. Design-kontraktsraden: design-contract.md | ✅ PL-T219 (legacy) · ✅ D-DISC 9e7fb823.m-1 · ✅ 9e7fb823.m-3 (profilen) · ✅ 9e7fb823.m-4 (web-ytan)
  • F15.06.11 Shipped

    Pickup-game schedule per club (återkommande pickup_slot + pickup_exception, expansion vid läs-tid i spelplatsens egen timezone) — NY HEMVIST: D-DISC GET /v1/discover/places/{slug}/pickup (anonym, cross-tenant, CDN-cache 300 s) + åtta tenant-skopade administrationsrutter bakom capabilityn discovery.pickup.write. has_pickup har nu sin enda skrivare (RefreshPlaceHasPickup, i mutationens transaktion, med svepets plikt E som nät), så m-1:s filter has_pickup=true är sant och inte längre ärligt tomt. Kontrakt: specs/api/endpoints/discover-pickup.md; legacy-formen PublicClubProfile.pickup_schedule[] är referens, ej kontrakt

    ✅ PL-T219 (legacy) · ✅ D-DISC 9e7fb823.m-2
  • F15.06.12 Shipped

    Visitor-friendly club flag — NY HEMVIST: venues.welcomes_visitors (ägare D-VENUE, skrivs genom dess befintliga PATCH /v1/venues/{id}, specs/api/endpoints/venues.md) och filtret welcomes_visitors=true på D-DISC:s GET /v1/discover/map, GET /v1/discover/nearby samt fältet på den publika profilen GET /v1/discover/places/{slug} (specs/api/endpoints/discover-places.md). WEB-HEMVISTEN: D-DISC 9e7fb823.m-4 — flaggan är ett filterchip (*Besöksvänliga*) och en radmarkering med ikon + text (färg aldrig enda signal) på den publika findern web /hitta-spel; chipet är en SERVERFRÅGA (welcomes_visitors=true), aldrig en klientsållning

    ✅ PL-T219 (legacy) · ✅ D-DISC 9e7fb823.m-1 · ✅ 9e7fb823.m-3 · ✅ 9e7fb823.m-4 (web-ytan)
  • F15.06.20 Shipped

    Viewer preference center (ViewerPreference: follow + push + digest, get-or-default)

    ✅ PL-T219
  • F15.06.21 Shipped

    Per-player follow + match alerts (cap 200 / 100 / 50, score-tröskel)

    ✅ PL-T219
  • F15.06.22 Shipped

    Per-club follow + news feed (digest-källa)

    ✅ PL-T219
  • F15.06.23 Shipped

    Personalised digest (DigestBuilderService: daily/weekly, idempotent dispatch-log)

    ✅ PL-T219
  • F15.06.24 Shipped

    Quiet hours + push-cadence grouping (IANA-tz, batched_5min/15min/60min)

    ✅ PL-T219

My Equipment

F15.07
Planned

Inspired by golf (club fitting) and bowling (ball specs) — track your boules and equipment.

How it works
  • F15.07.01 Shipped

    Boule inventory (brand, model, weight, diameter, hardness, striation pattern) — PL-F1507a

    ✅ D-COMMERCE 0fa09b8b.m-1 (greenfield; legacy PL-F1507a/b removed)
  • F15.07.02 Shipped

    Boule serial number tracking (for homologation verification) — PL-F1507a

    ✅ D-COMMERCE 0fa09b8b.m-1 (greenfield; legacy PL-F1507a/b removed)
  • F15.07.03 Shipped

    Equipment photo gallery — PL-F1507a

    ✅ D-COMMERCE 0fa09b8b.m-1 (greenfield; legacy PL-F1507a/b removed)
  • F15.07.04 Shipped

    Performance notes per boule set (which set for which conditions) — PL-F1507a

    ✅ D-COMMERCE 0fa09b8b.m-1 (greenfield; legacy PL-F1507a/b removed)
  • F15.07.05 Shipped

    Equipment wear tracking (usage log, matches played, surface type, total matches counter) — PL-F1507b

    ✅ D-COMMERCE 0fa09b8b.m-1 (greenfield; legacy PL-F1507a/b removed)
  • F15.07.06 Shipped

    Equipment wishlist and comparison (wishlist CRUD, compare 2–5 sets side-by-side) — PL-F1507b

    ✅ D-COMMERCE 0fa09b8b.m-2 (greenfield; legacy PL-F1507b removed)
  • F15.07.07 Shipped

    "What boules do the pros use" — equipment profiles of top players (search, filter, verified badge) — PL-F1507b

    ✅ D-COMMERCE 0fa09b8b.m-2 (greenfield; opt-in only; legacy PL-F1507b removed)

Weather & Conditions

F15.08
Planned

Inspired by golf and sailing — outdoor sport conditions matter.

How it works
  • F15.08.01 Shipped

    Weather forecast for upcoming competitions, block by block (the competition's timed schedule blocks, else morning/afternoon per day; beyond_horizon for days past the provider) with the federation's warning thresholds evaluated per block, plus the weather_forecast_warning notice (one per competition/day/threshold, measured outcome) sent by the sweep weather_forecast_sweep — GET /v1/public/competitions/{id}/weather-forecast

    ✅ 3e1559c7.m-1 (t-1)
  • F15.08.02 Shipped

    Weather conditions at a published place to play (temperature, feels-like, wind, gusts, humidity, rain risk — measured through the provider port, integers only, stale snapshot shown with its time) — GET /v1/discover/places/{slug}/conditions

    ✅ 3e1559c7.m-1 (t-1)
  • F15.08.03 Shipped

    Crowdsourced surface reports per place to play — a member's OBSERVATION (surface type, condition, hardness, wind on site, optional court, optional note and photo through the media port) in D-COURTSIDE's own table, never D-VENUE's register; name read at query time, 3/reporter/place/rolling day, own deletion within the freshness window, the fresh report weighed into playability — GET

    POST /v1/discover/places/{slug}/surface-reports, POST …:photo-grant, DELETE …/{id}, GET …/{id}/photo | ✅ 3e1559c7.m-2 (t-1)
  • F15.08.04 Shipped

    Playability score 0–100 per hour in the place's local time, every deduction in factors[], the recommended window and best hour — a pure function over the federation's weather_policy (GET/PUT /v1/tenant/weather-policy) — in the conditions response

    ✅ 3e1559c7.m-1 (t-1)
  • F15.08.05 Shipped

    Weather-related tips — a pure matching engine (MatchTips) over the current forecast hour and the fresh surface report against the federation's tip set (tenant data with a code-declared platform catalogue as source) — GET /v1/discover/places/{slug}/tips, federation management GET

    POST /v1/tenant/playing-tips, POST …:import-catalog, PATCH …/{id} | ✅ 3e1559c7.m-2 (t-1)
  • F15.08.06 Shipped

    Historical weather per place and calendar month from the platform's OWN daily records (weather_daily), with measured coverage (days + years) and no_data months — GET /v1/discover/places/{slug}/weather-history

    ✅ 3e1559c7.m-1 (t-1)

Social & Community Engagement

F15.09

Inspired by Strava, fitness apps, and social sport platforms.

How it works
  • F15.09.01 Shipped

    PL-F1509a

    ✅ PL-F1509a
  • F15.09.02 Shipped

    PL-F1509a

    ✅ PL-F1509a
  • F15.09.03 Shipped

    PL-F1509a

    ✅ PL-F1509a
  • F15.09.04 Shipped

    PL-F1509a

    ✅ PL-F1509a
  • F15.09.05 Shipped

    PL-F1509b

    ✅ PL-F1509b
  • F15.09.06 Shipped

    PL-F1509b

    ✅ PL-F1509b
  • F15.09.07 Shipped

    PL-F1509b

    ✅ PL-F1509b
  • F15.09.08 Shipped

    PL-F1509b

    ✅ PL-F1509b
  • F15.09.09 Planned

    PL-T098 (legacy)

    Group membership — legacy auto-provision per klubb byggs INTE; rebuilden: montering på valfri nod eller direkt under förbundet, medlemskap för vem som helst med en identitet, se F15.14.13
  • F15.09.10 Planned

    PL-T098 (legacy)

    Nested comments (max djup 2) med @-mentions och reaktioner — se F15.14.14
  • F15.09.11 Planned

    PL-T098 (legacy)

    Moderator-roller + ban + promote/demote med admin-only guards — se F15.14.13; sedan m-6 dessutom förbundets moderator (social:moderate, systemmallen social_moderator, tenant- eller nodskopad) och operatörens läs-only-insyn med safeguarding-eskalering (/v1/sys/social/{overview,cases}, sys.social.safeguard)
  • F15.09.12 Planned

    PL-T098 (legacy)

    Join request-flöde (join_policy=request, oavsett nivå) — se F15.14.13
  • F15.09.13 Planned

    PL-T098 (legacy)

    Content report på inlägg/kommentarer/meddelanden → ETT ärende per innehåll, tröskeln (3 rapportörer) döljer provisoriskt, moderatorns åtgärd (mark_safe|hide|delete|anonymize_author) genom ägarnas sömmar med audit; förbundets F1-6-granskning av tävlingsriktade lagannonser (team_listing_publish); rapportörens radering + 365-dygnsgallringen — se specs/api/endpoints/social-moderation.md, sys-social.md, social-team-listings.md §Annonsmoderering. Förbundets modereringskö i arrangörsappen (D-ADMINAPP 94f74ebf.m-14.t-8): admin-ytan /moderering (kön, grupperna, policyväxeln) är flyttad till packages/shared/src/views/social/moderering med två renderare och nås i arrangörsappen på /moderering med samma kontrakt (MOD_TESTIDS), samma URL-state (?flik=&arende=… läst och skriven i båda) och samma allowed_actions-dom — spelarappens *Rapportera* och modereringspanel är orörda; vy-spec specs/arrangor/views/moderering.md
  • F15.09.14 Planned

    PL-T098 (legacy)

    Per-grupp settings (visibility, who-can-post, join_policy, push_policy, åldersgräns; profanity-flaggan lagras, effekten är 0154301b.m-6) — se F15.14.13
  • F15.09.15 Planned

    PL-T098 (legacy)

    Fan-out policy (members/mentions-only/off) genom jobbet group_post_fanout och D-COMM:s pipe (in_app; push ägs av D-PUSH) — se F15.14.15
  • F15.09.16 Planned

    PL-T098 (legacy)

    GDPR: författaren anonymiseras (»Raderad användare«), tråden står kvar, foton rivs — se F15.14.16
  • F15.09.17 Shipped

    PL-T286

    ✅ PL-T286
  • F15.09.18 Shipped

    PL-T286

    ✅ PL-T286

Augmented Reality & Camera Features

F15.10

AR-powered tools for measuring, learning, detecting, and replaying petanque action on mobile.

How it works
  • F15.10.01 Shipped

    PL-F1510

    ✅ PL-F1510 (legacy) · Go-ombyggnad: LEVERERAD i 3e1559c7.m-3 som on-device stillbildsmätning (ingen server, ingen ArMeasurementSession) — /pa-banan?verktyg=matning: människan markerar lillen och varje boule (centrum + kant) i en bild tagen rakt uppifrån; avståndet kant till kant och en RÄKNAD osäkerhet per bild ur den rena measureDistance, domen *vem ligger* (tre utfall) ur whoIsHolding. Vägledande — vid protest gäller domarens fysiska mått. Domänregel: docs/domain/courtside/README.md §Mätregeln, BR33.08
  • F15.10.02 Shipped

    PL-F1510

    ✅ PL-F1510 (legacy) · Go-ombyggnad: LEVERERAD i 3e1559c7.m-4 — stillbild, regelbaserad segmentering med tre färgprov, ingen modell; /pa-banan?verktyg=kuldetektering detekterar lagens kulor on-device med RÄKNAD konfidens per kula och räknar rundans poängFÖRSLAG ur m-3:s avståndsfunktion (*för jämnt — mät* när marginalen inte räcker); förslaget förifylls i /poang:s certifierade inmatning (alltid redigerbart, skrivs bara av människans knapp) med bilden som fotobevis genom D-COURTSIDE:s register (POST /v1/courtside/photos:grant, bunden i rundans transaktion). Domänregel: docs/domain/courtside/README.md §Kuldetekteringen och poängförslaget, BR33.10—BR33.11
  • F15.10.03 Shipped

    PL-F1510

    ✅ PL-F1510 (legacy: AR-handledning med framstegsspårning) · Go-ombyggnad: LEVERERAD i 3e1559c7.m-5 som träningsöverlägg med facit — /pa-banan?verktyg=traningsoverlay: ett SKÄRMFÖRANKRAT överlägg (donnée-markör, kastbåge för pointing ur stilkatalogen roulette/demi_portee/portee, skottlinje för skytte) ritat ur den rena throwGuide över kamerans levande bild; kastet filmas utan ljud och facit ges i klotbredder (*kort/långt*, *vänster/höger*, med räknad osäkerhet) ur deviationFromTarget — en träningshjälp, inget mått. Legacy-handledningens framstegsspårning (ArTutorialProgress) byggs inte — ⊘ ägarbeslut. Domänregel: docs/domain/courtside/README.md §Träningsöverlägget, §Facit i klotbredder, BR33.12
  • F15.10.04 Shipped

    PL-F1510

    ✅ PL-F1510 (legacy: autodetektera cochonnet via datorseende) · Go-ombyggnad: LEVERERAD i 3e1559c7.m-4 — lillen detekteras av detectBoules (DetectedJack) i kuldetekteringen på /pa-banan?verktyg=kuldetektering, se F15.10.02. Raden rättad i 3e1559c7.m-5 (den pekade tidigare på repris & slowmotion, som är F15.10.05). Domänregel: docs/domain/courtside/README.md §Kuldetekteringen och poängförslaget, BR33.10
  • F15.10.05 Shipped

    PL-F1510

    ✅ PL-F1510 (legacy: slow-motion-repris med video_url/is_public) · Go-ombyggnad: LEVERERAD i 3e1559c7.m-5 som on-device-repris — /pa-banan?verktyg=repris: klippen spelas upp i ¼× · ½× · 1× med tidsstegning (≈ 1 bildruta), skrubber, in-punkt, loop och överlägget återritat ovanpå, och två kast jämförs sida vid sida (staplat på smal skärm) med EN kontrollrad. Klippen bor på enheten under husets offline-modell (MediaBytesStore + EN registerpost per identitet, rensade i den bekräftade utloggningen). Moln, is_public och publika repriser byggs inte — non-goal (ägarbeslut: ett videoarkiv på servern vore en egen domän med samtycke, frist och radering). Domänregel: docs/domain/courtside/README.md §Klippen på enheten, BR33.13

Premium Mobile-First UX

F15.11
Planned

Mobile-first redesign av spelar-appen: dual-mode shell (bottom-tab på mobil, sidebar på desktop), hero-baserade startvyer, offline-first licenskort och tenant-branded theming.

How it works
  • F15.11.01 Shipped

    Dual-mode app shell — useDeviceClass({ cutoff: 640 }) växlar mellan mobile bottom-tab-nav (5 slottar: Hemma / Matcher / Ranking / Licens / Mer) och desktop sidebar (260 px) + centrerat innehåll (max 1024 px) utan route-byte eller data-remount. app/src/components/navigation/AppShell.tsx, BottomTabNav.tsx, SidebarNav.tsx.

    ✅ PL-T157
  • F15.11.02 Shipped

    Hero-baserad startsida — nästa match som full-bredd-hero med LinearGradient (primary→secondary), Fraunces display-titel, countdown, CalendarClock/MapPin-meta och sticky CTA. Två-up MiniCards (Licens + Ranking) + senaste resultat + discover-getting-started. Pull-to-refresh. app/src/components/home/PlayerHome.tsx.

    ✅ PL-T157
  • F15.11.03 Shipped

    Fullscreen licenskort — hologram-gradient-ring (primary→secondary→accent), Fraunces display-titel, 220 px QrPlaceholder, valid-until + status-pin, offline-verification-banner. Läser från verifyLicense() som faller tillbaka på signerad JWT-bundle från cache när offline. app/app/(tabs)/license.tsx, app/src/components/license/QrPlaceholder.tsx.

    ✅ PL-T157
  • F15.11.04 Shipped

    Rankings med "Du är #N"-highlight + sparklines — highlight Card ovanför leaderboard med aktuell position + Movement + mini-sparkline; varje RankingRow har position, namn, klubb, Movement-indikator (ArrowUp/Down/Minus) och 56×18 SVG-sparkline. Current-user row har primary-tint-bakgrund + border. app/app/(tabs)/rankings.tsx.

    ✅ PL-T157
  • F15.11.05 Shipped

    Matcher-lista med status-badges — MatchRow med Round-badge (eller LIVE-pin för live-matcher), W/L-badge för finished, CalendarClock+MapPin-meta, score + chevron-CTA. Separata sektioner "Upcoming" och "Recent". app/app/(tabs)/matches.tsx.

    ✅ PL-T157
  • F15.11.06 Shipped

    "Mer"-fliken — overflow-meny för sekundära sektioner (Profile, Equipment, Discover, Search, Community, Weather, Settings). Logged-in identity-card med avatar + tenant-badges, sign-in-CTA för gäster, grupperade rows med ikon-bubbla + description. Logout-knapp längst ned. app/app/(tabs)/more.tsx.

    ✅ PL-T157
  • F15.11.07 Shipped

    Offline-banner på root-nivå — prominent <Banner variant="offline"> med WifiOff-ikon, "senast synkad HH:MM"-text och optional "Sync now"-CTA när pendingCount > 0. Separat från det tunna OfflineIndicator sync-strippen. app/src/components/OfflineBanner.tsx.

    ✅ PL-T157
  • F15.11.08 Shipped

    Tenant-branded ThemeProvider — useTenantBranding()-hook hämtar /me/tenant-profile vid activeTenantId-byte och applicerar theme-objektet via applyTenantTheme(). Caching per tenantId via lastAppliedRef — best-effort, inga fel bubblar upp. app/src/hooks/useTenantBranding.ts.

    ✅ PL-T157
  • F15.11.09 Shipped

    Banner-primitiv — ny <Banner>-komponent med variants info/success/warning/error/offline, optional icon (lucide), title, message, action-button och dismiss. Matchar admin-appens Banner-API men med offline-variant (warning-palett) unikt för app-paradigmet. app/src/components/ui/Banner.tsx.

    ✅ PL-T157
  • F15.11.10 Shipped

    Haptic-feedback-gating per interaktion — useHaptics().fire('selection') på alla bottom-tab-trycky, match-row-trycky, more-entry-trycky; fire('warning') på logout. Respekterar både accessibility-toggle och OS reduce-motion. app/src/hooks/useHaptics.ts.

    ✅ PL-T157
  • F15.11.11 Shipped

    QrPlaceholder — deterministisk 25×25 SVG-bitmap från licens-payload, tre klassiska QR-finder-patterns + FNV-1a-hash-seedad LCG. Dokumenterat ej scanbar; federationens skannare läser license-number från visibel text. Swappbar mot riktig QR-komponent utan ändring av omgivande layout. app/src/components/license/QrPlaceholder.tsx.

    ✅ PL-T157
  • F15.11.12 Shipped

    Pull-to-refresh på alla primärvyer — RefreshControl på Home, Matches, Rankings, License. Tint = colors.primary. Samma callback som initial load() — enhetligt refresh-beteende.

    ✅ PL-T157

Digital scoreboard / live-display (D-SCORE)

F15.12
Planned

> Initiativ **D-SCORE** (`docs/product/scoreboard-analysis.md`), milstolpar `0ad09f7c.m-1` (t-1 backend,

  • F15.12.01 Planned

    Display-datamodell — rena tenant-skopade tabeller display/display_court/display_pairing/display_event (migration 00074_display_core.sql). Enum-CHECKs för status/mode/theme/distance_profile; mjuka event_id/court_ref-referenser (ingen hård FK över domängräns); ingen sponsor/rental-modell i m-1.

    0ad09f7c.m-1.t-1
  • F15.12.02 Planned

    Display-administration (/v1/displays…) — skapa (idempotent, med initial pairing-kod) / lista / hämta / PATCH (If-Match, optimistisk lås → 409) / soft-delete (retired) / PUT …/courts / POST …/pairing / GET …/events. Capability-gated display:manage/display:view; tenant-isolering (404 för främmande tenant).

    0ad09f7c.m-1.t-1
  • F15.12.03 Planned

    Pairing & klient-bootstrap — POST /v1/display-pairings/{code}/claim (oautentiserad device → claim_token + config, engångs, hashad hemlighet), GET …/config (claim_token/display:view), POST …/heartbeat (idempotent, online/offline härlett ur last_seen_at).

    0ad09f7c.m-1.t-1
  • F15.12.04 Planned

    Sys tvärs-tenant flottöversikt — GET /v1/sys/displays (operatör-scope, läs-only): aggregerade antal online/offline/unpaired/retired + rad per display i alla tenants.

    0ad09f7c.m-1.t-1
  • F15.12.05 Planned

    AuthZ-capabilities display:manage/display:view i katalogen, seedade på clubadmin/federation_admin. OpenAPI-först + genererad TS-klient i synk (packages/shared).

    0ad09f7c.m-1.t-1
  • F15.12.06 Shipped

    admin Displayer-sektion (admin/app/displayer; sedan D-ADMINAPP 94f74ebf.m-3.t-5 vy-modulen packages/shared/src/views/competition/displayer med web- och native-renderare — också i arrangörsappen /displayer; vy-spec specs/admin/views/displayer.md) — lista med status-badge färg+ikon+text (online/offline/unpaired/retired) + filter, skapa/para-stepper (namn → org-nod → banor → visningsläge → tema/distans → generera & visa pairing-kod med regenerera), redigera-konfig (PATCH med If-Match) och händelse-tidslinje. Verklig data via den genererade klienten (displaysOps); inga stubbar. Sponsor-steg (m-4) och renderad live-vy (m-2) är medvetet inte med.

    0ad09f7c.m-1.t-2
  • F15.12.07 Shipped

    sys tvärs-tenant flottöversikt (sys/app/flotta) — operatör-scope, läs-only, ingen branding: aggregerade antal + tabell över alla tenants displayer ur GET /v1/sys/displays via genererad klient. Ingen beslutsrätt i tenants konfig.

    0ad09f7c.m-1.t-2
  • F15.12.08 Planned

    SSE live-stream-relä — GET /v1/displays/{id}/stream (text/event-stream): leverantörsneutral in-process-relä av D-COMP:s frysta vokabulär snapshot\

    score|match_status|standings (§5d.2, aldrig score_update/mene_completed/connected/update), projicerat/filtrerat per court_ref mot displayens display_court-mängd. Handshake (claim_token eller same-tenant display:view) före strömmen öppnas → 403 utan auth, 404 främmande tenant. snapshot först (subscribe-före-send); config-PATCH/PUT courts pushar färsk snapshot till anslutna klienter. Mock-stream (?mock=1&mock_courts=…, test/scaffolding) kör D-COMP-fritt. Spec: specs/api/sse/events-display.md. | 0ad09f7c.m-2.t-1
  • F15.12.09 Planned

    Snapshot polling-fallback — GET /v1/displays/{id}/snapshot: materialiserad visnings-ställning (kort-TTL ETag), samma handshake som strömmen, svarar även utan aktivt live-event (senast kända / tom), endast visnings-data (inga internfält). OpenAPI + genererad TS-klient i synk.

    0ad09f7c.m-2.t-1
  • F15.12.10 Shipped

    Fullscreen display-klient (web/app/d/[displayId], shell-lös BYO-skärm) — pairing-vy ("Ange koden"), device-claim (POST /v1/display-pairings/{code}/claim, claim_token i localStorage + X-Display-Claim-Token), SSE-konsument via fetch+ReadableStream.getReader() av det frysta vokabuläret (snapshot\

    score|match_status|standings), tre display-axlar ur config (visningsläge single|hall_overview|rotation|feature_ticker · distans-profil near|medium|far · tema indoor|outdoor), upplösnings-oberoende (vw/vh/clamp) med enorma tabular-nums-siffror, status-badge färg+ikon+text (WCAG 2.2 AA), vit-etikett-branding {logo,name,primary,accent}, offline-resiliens (cachad ställning + "återansluter…" + backoff-reconnect + /snapshot-polling, aldrig blank), cast-mottagar-läge. Sponsor-region reserverad men tom (m-4-gräns). | 0ad09f7c.m-2.t-2
  • F15.12.11 Shipped

    Admin renderad förhandsvisning (admin Displayer-sektion) — samma display-variant-rendering mot displayens config (mode/theme/distans/banor) driven av /stream (admin-bärare) eller mock-strömmen (?mock=1) när inget live-event är kopplat; tydligt märkt demo/tom-ställning, inga hårdkodade siffror, sponsor-region tom. Fyller den yta m-1 medvetet lämnade.

    0ad09f7c.m-2.t-2
  • F15.12.12 Shipped

    Publik oautentiserad projektions-yta — GET /public/scoreboard/{id} + /stream: ett publik-projektions-lager ovanpå m-2:s relä/snapshot, nås av vem som helst med displayens UUID (ingen bearer/claim_token). 200 med endast en dokumenterad allowlist av visnings-data (mode/theme/distance_profile/rotation/event_id, banor, matcher {match_id,status,court_ref,score}, standings publik projektion) + white-label branding-tokens {logo,name,primary,accent}; inga internfält (PII/tenant-interna/aktör-id/responder/pager/audit) — en strikt internfält-strippad delmängd av device-ytan. Kort-TTL cachebar (ETag + Cache-Control max-age); okänd/retired → 404; utan live-data → 200 tom (aldrig blank). Publika strömmen bär samma frysta vokabulär + allowlist. OpenAPI + genererad TS-klient i synk. Registrerad via sanktionerat /public/*-mönster (F1-4, aldrig --no-verify). Ingen ny tabell/kolumn. Spec: specs/api/endpoints/displays.md, specs/api/sse/events-display.md. web/app-konsument-vyer levereras i m-3 t-2/t-3. Sponsor-slot + impression-rollup till F11.03 = m-4 (backend sponsor_slot/impression-beacon/rollup = m-4 t-1; frontend-delen = m-4 t-2: sponsor-regionen renderas ur verklig sponsor-data i display-variantens sponsor-region på device-fullscreen (ScoreboardView), publik web-live-vy (PublicScoreboardClient) och app-spectator-ytan — aldrig platshållare, tom när ingen aktiv slot — och varje visning kallar den idempotenta impression-beaconen (device POST /v1/displays/{id}/sponsor-impression, publik/app POST /public/scoreboard/{id}/sponsor-impression) med en stabil dedup_key per (slot, session, tids-bucket) så reconnect/poll/re-render aldrig dubbelräknas; admin kopplar/redigerar/frikopplar sponsor-slot via ett sponsor-steg i Displayer-steppern + redigera-panelen med live-förhandsvisning, allt via den genererade TS-klienten).

    0ad09f7c.m-3.t-1
  • F15.12.13 Shipped

    Publika konsument-vyer (web + app) ovanpå F15.12.12:s projektion. web: oautentiserad SSR/SEO full-white-label publik event-live-vy (web/app/scoreboard/[displayId]), castbar/fullscreen, aldrig blank (cachad ställning + "återansluter…" + /public-snapshot-polling + backoff). app: publik-spårets Live-matchlista (LIVE-pin ikon+text) + per-match mène-progression härledd ur det frysta vokabuläret via den återanvända scoring-presenter; follow som PERSISTERAS via den rebuildade viewer-follow-primitiven POST/GET/DELETE /v1/me/follow (F15.06.20–22: follow + push + digest, notify_push/notify_digest) så D-PUSH kan konsumera den — notis-LEVERANSEN själv byggs ej här (D-PUSH/D-COMM). + liten klubbadmin para/justera-display on-the-go-yta (capability-gatad display:manage, POST /v1/displays/{id}/pairing + PATCH med If-Match) via den genererade klienten. Migration 00075_viewer_follow.sql (self-scopad /me-yta, ingen display/sponsor/hyr-tabell). Spec: specs/api/endpoints/me-follow.md.

    0ad09f7c.m-3.t-2

Socialspel: spontant gängspel med QR-join (D-SOCIALSPEL)

F15.13
Planned

> Initiativ **D-SOCIALSPEL** (ägarbeslut 2026-07-23) — gratisnivåns flaggskepp i player-appen

  • F15.13.01 Planned

    Lottningsmotorn (api/internal/socialplay, ren/DB-fri/klockfri) — matchformerna 1v1 (valbar, default av) / 2v2 / 2v3 / 3v3 med täckningsmatten (alla N ≥ 4 utom exakt 7 med 2v3 på), vilorotationens hårda invariant (ingen vilar två gånger innan alla vilat en), de fyra parningslägena (omlottning+variation, prestandagrupper, Monrad med omötesgaranti, fasta lag+variation), A/B-splitten med separat balansering per pool och deterministiskt seed per runda. Låst testsvit. Inkopplad i m-3 (F15.13.14): motorfilerna är oförändrade, sessionsadaptern ligger i en ny fil.

    659e6203.m-1.t-1/t-2
  • F15.13.02 Planned

    Datamodell social_play_session + social_play_member (migration 00138_dsocialspel_m2_sessions.sql) — ingen tenant_id, enda externa FK: auth_identity; CHECK identitet eller gästnamn, joined_via IN ('qr','manual','web'), unikt share_token, partiellt unikt index (session_id, auth_identity_id) som gör konto-joinet idempotent på DB-nivå. rests/pool anstådda till m-3 (inget oanvänt schema).

    659e6203.m-2.t-1
  • F15.13.03 Planned

    Starta & redigera session — POST/GET/PATCH /v1/social-play/sessions[/{id}] med visionens hela inställningsuppsättning i settings jsonb (forms, allow_2v3, allow_1v1 default av, rounds 1–10 eller null="öppen", target_score default 13, team_mode, pairing_mode, ab_split, split_round) — exakt m-1-motorns enumvärden, partiella objekt fylls med defaults/befintliga värden, fältvalidering med stabila 422-koder. Default-namn "Socialspel [datum]"; ägaren ur bearer-claimet, aldrig ur bodyn. settings_locked förbereder m-3:s låsning utan API-brott.

    659e6203.m-2.t-1
  • F15.13.04 Planned

    Join-token + QR-URL (POST …/{id}/join-token, api/internal/socialplay/jointoken) — check-in-tokenets HMAC-mönster med härledd nyckel (kryptografisk domänseparation: ett check-in-token kan aldrig spelas upp som join-token, även med samma råa hemlighet i båda env-nycklarna), flergångs (hela gänget skannar samma skärm) och roterande (skärmen präglar om före utgång; äldre giltigt token fortsätter gälla), TTL strukturellt klampad ≤ 10 min, ingen PII i payloaden, tom nyckel ⇒ typat 5xx (aldrig osignerat token). join_url = <web>/socialspel/<share_token>?t=<token>.

    659e6203.m-2.t-1
  • F15.13.05 Planned

    Gå med med konto (POST …/{id}/join) — idempotent (samma identitet igen ⇒ 200 och SAMMA rad), ägaren får gå med utan token (arrangör ≠ spelare), typad felsplit 401 manipulerat / 409 utgånget / 409 fel session / 409 stängd anslutning / 409 medlemstak (200 per session). Signatur verifieras före utgång.

    659e6203.m-2.t-1
  • F15.13.06 Planned

    Gäst utan konto (POST /v1/public/social-play/shared/{share_token}/join) — helt oautentiserad: färskt join-token + ETT fält (namn, trimmat 1–80 tecken) ⇒ deltagare utan identitet (joined_via='web'). share_token ensamt räcker aldrig (läsbehörighet ≠ skrivbehörighet) — separationen är det som håller delningslänken skrivskyddad. Stabilt rad-id för framtida gäst-claim.

    659e6203.m-2.t-1
  • F15.13.07 Planned

    Ägarens deltagarhantering — manuell inskrivning (POST …/{id}/members, fungerar även när anslutningen är stängd — sena ankomster), borttag (DELETE …/members/{member_id}, sessionsskopat ⇒ främmande rad 404) och öppna/stäng anslutning (POST …/{id}/joining, skilt från status så en pågående session kan öppna igen mellan rundor).

    659e6203.m-2.t-1
  • F15.13.08 Planned

    Publik skrivskyddad delningsläsning (GET /v1/public/social-play/shared/{share_token}) + åtkomstmodellen: handler-grindar (ej RLS) med normativ 404-före-403-ordning (utomstående får aldrig ett existensbesked; bara en verklig deltagare kan se 403; oautentiserad ⇒ 401). Publika medlemsraden är vitlistad (id, display_name, is_guest, joined_via) — aldrig e-post, aldrig auth_identity_id.

    659e6203.m-2.t-1
  • F15.13.09 Planned

    Deltagarlistan live (SSE GET …/{id}/live för ägare/deltagare + GET /v1/public/social-play/shared/{token}/live för gästvyn) — varje frame är event: snapshot med HELA läget {session, members, member_count}, vid connect och vid varje medlems-/anslutnings-/namn-/inställningsändring (≤ 5 s). Fulla snapshots gör återanslutning trivialt korrekt; icke-blockerande fan-out + 15 s heartbeat (husets hubb-mönster).

    659e6203.m-2.t-1
  • F15.13.10 Planned

    "Mina socialspel" (GET /v1/social-play/sessions) — sessioner jag äger eller deltar i, nyast först, med role och member_count; tom lista i stället för 404. Underlaget för appens ingång från hemskärmen.

    659e6203.m-2.t-1
  • F15.13.11 Planned

    Kontrakts- och integritetsvakter — felkoderna registrerade i problem.Registry (openapi x-pl-codes-driftvakt), openapi-först + genererad TS-klient i synk (packages/shared), gratisnivån bevisad av en schema-assertion (en tenant_id på socialspelstabellerna är ett kontraktsbrott), GDPR-erasure-domäner (personens egna deltagarrader + de sessioner hen startade; gästrader rörs ej) och det dokumenterade no-audit-beslutet (audit_log.tenant_id är NOT NULL och socialspelet är tenant-löst — resultatens ändringshistorik får egna tabeller i resultatmilstolpen).

    659e6203.m-2.t-1
  • F15.13.12 Shipped

    Ytorna. App (app/app/socialspel/ + hemskärmsingången home-socialspel): *Mina socialspel* (ägda eller deltagna, senast-först, ärligt tomläge), *Starta socialspel* (hela inställningsuppsättningen med defaults — matchformer, 2v3 på, 1v1 av, rundor 1–10/"öppen", målpoäng 13, lagbildning, rundparning, A/B + splitrunda), *sessionsvyn* med stor lokalt ritad QR som förnyar sig själv (om-prägling ~60 s före exp, join-länkens element byts aldrig ut), live deltagarlista, alla fyra ägarkontrollerna (manuell inskrivning som fungerar även vid stängd anslutning, borttag, öppna/stäng och redigera inställningar — samma delade fältuppsättning som starten, förifylld med sessionens FAKTISKA settings och skickad som PATCH med skapandets body, ärligt spärrad vid tomt namn eftersom servern då behåller det gamla) plus "Jag spelar också" för arrangören som själv spelar (ägar-joinet utan token ⇒ deltagarrad med identitet; läget härlett ur serverns member_count kontra members, aldrig gissat) och ingen "Lotta"-CTA (m-3:s yta stubbas inte in), samt *join-rutten* /socialspel/ga-med?st=…&t=… bakom QR-länken (publik delningsläsning → identitetsskopat, idempotent join → sessionsvyn). Web (web/app/socialspel/[share_token]): kontolös mobil-först gästvy — skrivskyddad live-läsning på delningsnyckeln, "Gå med som gäst" med ETT fält när tokenet är färskt och anslutningen öppen, alla ärliga lägen (stängd anslutning släcks live utan omladdning; utgånget token upptäcks före klicket — exp avkodas ur tokenets osignerade, PII-fria payload med serverns egen gräns now >= exp plus en timer på utgången, så ett dött token aldrig renderar formuläret medan servern förblir auktoritet; saknat/oläsbart token, okänd nyckel, typade join-fel), deltagarräknaren som räknar exakt raderna i listan (personantalet inkl. arrangören redovisas separat och benämnt) "Öppna i appen" som riktig länk i appens schema (petanquelife://socialspel/ga-med?st=…&t=…, A-4:s realisering av visionens universella länk — den som har appen går med med sin identitet i stället för som identitetslös gäst, och länken blockerar aldrig gästvägen under den) och rekryteringskroken CTA "Skapa konto" som riktig länk till husets individual-signup; noindex. Live på båda ytorna via t-1:s SSE-snapshotkanaler genom transportens requestEventStream (≤5 s utan omladdning, uppmätt i L4) med ärlig polling-fallback var 2:e sekund där strömmen inte kan hållas (Hermes). All trafik via den genererade klienten (socialPlayOps, ny barrel-rad) — L3d-ratchet oförändrad på baseline 0. Vy-specar: specs/app/views/socialspel.md, specs/web/views/socialspel-gastvy.md. Bevis: L4-drivrutinen tools/gate/socialspel-m2-e2e.mjs (hela resan + axe på gästvyn i 375×812 + DB-facit) samt enhetstester för de rena härledningarna (app/lib/__tests__/socialspel-presenter.test.ts — formulär ⇄ settings som inverser; web/lib/__tests__/socialspel-links.test.ts — app-djuplänkens schema/path/parametrar).

    659e6203.m-2.t-2
  • F15.13.13 Planned

    Kvällens avslut — backend (migration 00140_dsocialspel_m4_close.sql: ended_reason/rematch_of_session_id/last_activity_at på sessionen, emit-bokföringen social_play_result_event och anspråkstabellen social_play_guest_claim; fortfarande ingen tenant_id, enda externa FK-mål auth_identity). Avslutet är EN kodväg (api/internal/socialplayend): ägarens POST …/{id}/end och autostängningsjobbet social_play_autoclose_tick (varje timme, singleton, 24 h utan aktivitet, ended_reason='auto' och tom aktör i loggen) delar transaktion, session_end-loggpost och emit — avslutet är idempotent (andra anropet ⇒ 200, oförändrad stämpel, ingen ny logg, ingen andra emit). Skrivskyddet efter avslut ligger i den gemensamma sessionsupplösningen, före kroppsvalideringen (409 social_play_session_ended på hela mutationsytan; acceptanstestet genererar listan ur openapi, så en glömd rutt fäller sig själv). Summeringen rider med i sessionsdetaljen, den publika share-vyn och varje live-snapshot — men bara när kvällen är avslutad (ingen tom stub under spel): pall ur serverns tabellordning med place/tied (två pallar och två vinnare vid A/B), kuriosa (längsta vinstsvit där vila varken bryter eller förlänger, Fanny = förlorarens nolla) — båda sektionerna utelämnade när de saknar innehåll — plus kvällens siffror inkl. ärligt redovisade orapporterade matcher. Revansch (POST …/{id}/rematch) duplicerar kvällen i en transaktion (samma inställningar, nytt share_token, spår tillbaka; konton behåller identitet, gäster kopieras som gäster, den som lämnat kopieras inte, fasta lag kopieras aldrig) och svarar 200 på dubbeltryck i stället för att skapa en dubblett. §5d.2-emitten sker efter commit, bakom recover(), med persisterad idempotensbokföring skriven före emitten — en rad ⇒ aldrig en andra emit, i någon process eller kodväg — och en match där en sida saknar kontodeltagare emitteras inte alls (men bokförs). Gäst-claim (POST /v1/social-play/shared/{share_token}/guest-claims med bearer — aldrig en ny publik mutationsrutt — plus ägarens lista och beslut): ett godkännande sätter identiteten på samma medlemsrad (id:t ändras aldrig), supersedar konkurrenter, loggas — och emitterar ingenting (ingen retroaktiv ELO i v1); tak: ett väntande anspråk per anspråkare och session (partiellt unikt index) + 50 per session. GDPR: anspråkets identitet är registrerad som subject_direct i raderingsdomänerna och i täckningsmanifestet — raderas personen försvinner anspråket, medan gästraden i någon annans kväll aldrig rörs. Gränsdragning: ytorna för avslutet (summeringsvy, pall, Revansch-knapp, gästvyns summeringsläsning) är t-2 och stubbades inte in här — de är levererade i F15.13.16.

    659e6203.m-4.t-1
  • F15.13.14 Planned

    Spelet — backend (migration 00139_dsocialspel_m3_rounds.sql: social_play_round, social_play_match, social_play_team, social_play_change_log, banetiketter + rests/pool/status/team_id på medlemsraden; fortfarande ingen tenant_id, enda externa FK-mål auth_identity). Motorn inkopplad: POST …/{id}/rounds bygger RoundInput ur inställningarna (formmängd forms ∪ 2v3/1v1, preferens härledd ur forms), de aktiva deltagarna/de fasta lagen, den ackumulerade historiken och ett crypto/rand-seed, och persisterar runda + matcher + vilo-räknare + (första lottningen) status='playing'/joining_open=false/settings_locked=true i EN transaktion; unikheten (session_id, round_no) är samtidighetsvakten (två parallella lottningar ⇒ exakt en runda, förloraren får typad 409). Motorns *ValidationError mappas 1:1 till sju stabila 422-koder — aldrig ett läckt Go-fel, aldrig en 500. Fasta lag bildas vid första lottningen med laglig sidstorlek (överblivna = "utan lag", deltar inte) och byts därefter atomiskt med PUT …/teams. Resultat (POST …/matches/{id}/result): deltagaren rapporterar sin EGEN match, ägaren alla; sist skrivna gäller; målpoängvalidering med ägar-åsidosättande (lika poäng avvisas alltid); varje skrivning appendas till den append-only ändringsloggen (GET …/changes, aktör = visningsnamn, aldrig e-post/identitets-id). Tabellen aggregeras i ett rent steg och ordnas av motorns EGEN komparator (individuell/lagvis, separata A- och B-tabeller efter splitrundan) och rider med i sessionsdetaljen, den publika share-vyn och varje live-snapshot tillsammans med aktuell runda — ingen /standings-rutt, ingen andra sorteringsregel. A/B klassificeras med ClassifySplit i samma ögonblick som splitrundan blir färdigrapporterad; ägarens flytt är en override som alltid vinner. Deltagarförändringar under spel: sen ankomst startar på minsta vilo-räknaren, en deltagare som HAR spelat raderas aldrig utan markeras "har lämnat" (resultat och tabellrad bevaras). Determinism: rundans seed (decimalsträng på tråden) + input_member_ids/input_team_ids gör en persisterad runda mekaniskt replaybar. Gästvyn får GET /v1/public/social-play/shared/{token}/rounds — läsning, aldrig skrivning; ingen ny publik mutationsrutt (mekaniskt vaktat i openapi-testet).

    659e6203.m-3.t-1
  • F15.13.15 Shipped

    Spelet — ytorna. App: ägarens lottnings-CTA i sessionsvyn (socialspel-lotta "Lotta första/nästa runda" — eller, när lottning inte är möjlig, förklaringen socialspel-lotta-blockerad i dess ställe: för få deltagare för minsta lagliga matchform, uppnått rundtak, avslutad session; ingen död knapp någonstans) med serverns 409 social_play_round_incomplete som enda källa till "föregående runda är orapporterad" ⇒ "Lotta ändå" (avbruten match/tidsstopp), och låsta inställningar efter första lottningen (redigeringen visar bara det ändringsbara och PATCH-bodyn bantas mekaniskt till de olåsta nycklarna — servern 409:ar på en låst nyckel även med oförändrat värde). Rundvyn (app/app/socialspel/[id]/runda.tsx): rundans matcher med lag, bana (nummer + arrangörens namn), ställning, "Rapporterad av …"/"Rättad av …", omöten och grupp vid A/B — egna matchen överst som fältkontrakt (socialspel-egen-match först i DOM-ordningen; "vilken rad är jag" härleds ur mitt eget visningsnamn mot kontoraderna och bara när matchningen är entydig, eftersom deltagar-DTO:n är PII-fri) — resultatinmatning bara för den som får skriva (kontodeltagaren sin egen match, ägaren alla; inget inaktiverat fält för övriga), målpoängvalidering med ägarens åsidosättande (lika poäng aldrig), vilande med rotationsregeln utskriven, och ägarens justeringar (byt sida, ställ vilande, sätt in vilande i en orapporterad match, bannamn) som alla loggas i den append-only ändringsloggen. Tabellen ([id]/tabell.tsx): raderna i serverns ordning (motorns komparator är produktens enda sorteringsregel), individuell/lagvis enligt läge, A/B-flikar med ägarens manuella pool-flytt (overriden vinner alltid), rundhistorik och en accessibilityLabel per rad som läses som en mening (m-2:s öppna a11y-fynd (a) stängt för m-3:s ytor). Rundtimern (FR-9): nedräkning på arrangörens skärm — default 45 min, start/paus/nollställ, tydligt slutläge — helt lokal: ingen rutt, inget serverfält, ingen synk, ingen koppling till D-COMP-TIMER, och tillståndet bor utanför snapshot-härledd state så en inkommande live-frame aldrig nollställer den (vald längd minns i AsyncStorage — bara en siffra). Web: gästvyn utvidgad med den skrivskyddade läsytan — aktuell runda, live-tabellen (A/B-växling) och rundhistoriken, allt utan omladdning; ytan har ingen resultatinmatning, inte ens inaktiverad (visionens FR-4-ägarbeslut: rekryteringsincitament + missbruksskydd), utan säger i klartext att rapportering kräver konto (socialspel-gast-rapport-info) intill "Skapa konto" — mekaniskt vaktat: L4 asserterar att socialspel-resultat-* saknas helt i gästvyns DOM. Live på alla tre ytorna ur samma snapshot (rundan + tabellen ligger i varje frame; rundhistoriken bakom sin egen läsrutt, omläst när rundnyckeln ändras — spec A-12) med ärlig polling-fallback. Ärlig gräns: ingen "Avsluta"-CTA, ingen pall, ingen Revansch — avslutet är F15.13.13 och stubbas inte in. All trafik via den genererade klienten; L3d-ratchet oförändrad på baseline 0. Vy-specar: specs/app/views/socialspel.md (inkl. testid-kontraktet), specs/web/views/socialspel-gastvy.md. Bevis: L4-drivrutinen tools/gate/socialspel-m3-e2e.mjs (descriptor e2e.d/680-socialspel-m3.mjs) — hela resan mot körande stack + riktig Postgres: lottning → rundvy → deltagarens rapport från egen inloggning → ägarens rättning → live ≤5 s hos ägare, deltagare och gäst → gästvy utan inmatning → timern räknar ned → runda 2 med vilande → rundhistorik → axe wcag2a/aa/21aa/22aa i 375×812 utan critical/serious → DB-facit; plus enhetstester för de rena härledningarna (app/lib/__tests__/socialspel-spel-presenter.test.ts, web/lib/__tests__/socialspel-presenter.test.ts).

    659e6203.m-3.t-2
  • F15.13.16 Shipped

    Avslutet — ytorna. App: *"Avsluta kvällen"* (socialspel-avsluta) på sessions- och rundvyn med bekräftelsesteg (socialspel-avsluta-bekrafta) som säger exakt vad som händer — skrivskyddat efteråt, orapporterade matcher räknas ingenstans — och som bara finns för ägaren (en deltagare ser ingen knapp alls, inte ens inaktiverad; drivrutinen asserterar frånvaron). *Summeringen* (app/app/socialspel/[id]/summering.tsx): pall (två pallar och två vinnare vid A/B, placering i text inkl. "Delad 2:a plats"), kuriosa (utelämnad när servern inte bär någon), kvällens siffror med ärligt redovisade orapporterade matcher, sluttabell i serverns ordning, rundhistorik, avslutsorsak ("Avslutades automatiskt efter 24 timmar utan aktivitet" vid ended_reason='auto'), delningslänken ur appens ENDA härledning (shareLink() — serverns joinURL utan join-beviset; basadressen konfigurerad i varje eas.json-byggprofil som EXPO_PUBLIC_WEB_URL, mekaniskt asserterat så inget riktigt bygge delar ut en localhost-länk) med ärligt utfall på kopieringen (lyckat, misslyckat, och ingen knapp alls där urklippsväg saknas) och Revansch (ny kväll med samma deltagarlista, dubbeltryck ⇒ ingen dubblett). *Mina socialspel* delad i pågående (→ spelet) och avslutade (→ summeringen, med vinnaren ur serverns pall och tyst där summeringen inte lästs). *Gäst-anspråket*: samma rutt /socialspel/ga-med utan färskt ?t= blir anspråksvyn — anspråkaren väljer sin gästrad (socialspel-claim-valj) och skickar (socialspel-claim-skicka), ägaren godkänner/avslår i summeringen (socialspel-claim-godkann/-avsla); vyn läser kvällen via den publika delningsläsningen och skickar med bearer, eftersom anspråkaren per definition inte är deltagare (404-före-403). Web: gästvyn blir efter avslut kvällens permanenta resultatsida på samma URL — avslutsorsak, pall, kuriosa, sluttabell, rundhistorik och raden om att ett konto gör det möjligt att göra anspråk på gästresultaten via arrangören (socialspel-gast-claim-info) — fortfarande med noll socialspel-resultat-* i DOM:en och utan join-formulär (kvällen är stängd; ingen attrapp). Live på alla ytor ur samma snapshot: växeln är serverns summary (finns först vid status='ended'), aldrig en lokal "jag tryckte avsluta"-flagga — därför byter ägarens, deltagarens och gästens skärm samtidigt utan omladdning. All trafik via den genererade klienten; L3d-ratchet oförändrad på baseline 0. Vy-specar: specs/app/views/socialspel.md (Del III + testid-kontraktet), specs/web/views/socialspel-gastvy.md (summeringsytan efter avslut). Bevis: L4-drivrutinen tools/gate/socialspel-m4-e2e.mjs (descriptor e2e.d/690-socialspel-m4.mjs) — hela AC-12-resan mot körande stack + riktig Postgres: end utan auth ⇒ 401 → avslut via bekräftelsen → summeringen komplett → gästens fönster växlar live utan omladdning → delningslänken i ett utloggat fönster → anspråk skickat av en icke-deltagare och godkänt → Revansch med samma deltagarlista → pågående/avslutade → axe wcag2a/aa/21aa/22aa i 375×812 utan critical/serious → DB-facit (ended_reason, §5d.2-raderna, rematch_of_session_id, den godkända gästraden, 0 väntande anspråk, ingen tenant_id); plus enhetstester för de rena härledningarna (app/lib/__tests__/socialspel-summering-presenter.test.ts, app/lib/__tests__/socialspel-links.test.ts, web/lib/__tests__/socialspel-summering-presenter.test.ts).

    659e6203.m-4.t-2

Socialt & lag: lagannons och lagbildning (D-SOCIAL)

F15.14
Planned

> Parent: initiativet D-SOCIAL (`docs/migration/initiatives/D-SOCIAL.md`) → milstolpe `0154301b.m-1`. Kontrakt: `specs/api/endpoints/social-team-listings.md`, `specs/api/endpoints/social-teams.md`. Domän: `docs/domain/social/README.md`. Ersätter legacyraden F04.07.18.

  • F15.14.01 Planned

    Lagannonsen (/v1/social/team-listings, migration 00289_dsocial_m1_team_listings.sql): lägg, sök (cursor + bracket-ops, synligheten i FRÅGAN), läs, ändra (D-5) och avbryt. Lagstorleken ur referenskatalogen game_format (tête-à-tête kan inte annonseras), tenanten HÄRLEDD ur en valfri mål-tävling (riktad ⇒ bara tenantens medlemmar; annars cross-tenant), obligatorisk frist. Självguardad — fungerar för en klubblös identitet utan ett enda medlemskap. Inga namn i den publika listan.

    0154301b.m-1.t-1
  • F15.14.02 Planned

    Svar, inbjudan, accept — intresseanmälan med självdeklarerad position/nivå/tillgänglighet och en MÄTT matchningsgrad (match_score_bps + match_basis, null när inget gick att jämföra), ägarens inbjudan, svararens accept/avböj/tillbakadragande, GET /v1/me/invitations och GET /v1/me/team-listings.

    0154301b.m-1.t-1
  • F15.14.03 Planned

    Laget (/v1/social/teams) — bildas i accept-sömmens transaktion när sista platsen accepteras (aldrig tyst; triggern vägrar ett överfyllt lag), läses i D-COMP-konsumerbar form (registration_form, entrant_type=team, player_profile_id i lagets tenant) med djuplänken till /anmalan; upplös (kapten) och lämna (medlem). Kompositionsregeln är EN funktion: competition.ValidateSquadComposition.

    0154301b.m-1.t-1
  • F15.14.04 Planned

    Beskeden — fyra in_app-typer genom D-COMM:s pipe efter commit, utfallet MÄTT på social_notice (single-fire); en klubblös mottagare bokförs ärligt no_tenant_membership.

    0154301b.m-1.t-1
  • F15.14.05 Planned

    Utgången — läsregeln (en passerad frist är expired i samma sekund) + svepet team_listing_expiry_tick (var 15:e minut, single-fire, systemaktör).

    0154301b.m-1.t-1
  • F15.14.06 Planned

    Fixturen och kunskapen — seed-social (fyra identiteter, annonser i alla fyra lägen genom sömmarna, mätta besked, konvergent), kunskapsguiden social-matchmaking (sv + en), GDPR-domänen social.

    0154301b.m-1.t-1
  • F15.14.07 Planned

    Appytan /socialt (lagannonser, inbjudningar, mina annonser, mitt lag, ny annons) och panelen *Ditt lag ur Socialt* i /anmalan.

    0154301b.m-1.t-2
  • F15.14.08 Shipped

    Smarta matchningar i appen (0154301b.m-2) — sektionen *Smarta matchningar* på /socialt är LEVANDE (m-1:s ⊘ hävt): förslagskort ur GET /v1/me/social/suggestions som visar ANNONSEN (titel, spelform, *söker N av M*, sökt nivå/position ur katalogen, tid, plats, mätt avstånd), poängbandet som ikon + text med *NN %*, underlaget i klartext ur serverns axelord och min nivå med källan (*mätt ur ranking*/*uppgiven av dig*). *Intresserad* ⇒ intresseformuläret ⇒ respond, *Inte aktuellt* ⇒ dismiss (If-Match + Idempotency-Key), tomt-läget med serverns empty_reason, djuplänken ?forslag=. Vy-spec: specs/app/views/socialt.md §Smart matchning. Bevis: tools/gate/dsocial-m2-e2e.mjs.

    0154301b.m-2.t-2
  • F15.14.09 Shipped

    Nära mig, Nivå och Fler filter (0154301b.m-2) — chippen är SERVERFRÅGOR: positionen per session ur D-DISC:s usePosition() (nekad ⇒ eget förklarat läge, aldrig ett tomt resultat) med den delade radieväljaren över serverns slutna mängd (⇒ lat/lng/radius_km, serverns 422 vid chippen), bandväljaren ur katalogen (⇒ criteria.level_band[in]), region och datumintervall. Annonsraderna i serverns ordning med *»N,N km bort«*; *Mina annonser* visar nivån som uppgiven och mätt samt *»avståndet mäts inte längre — annonsen flyttade«*; *Ny annons* får *Använd min position* och *Max avstånd*. Ingen lagring i klienten.

    0154301b.m-2.t-2
  • F15.14.10 Shipped

    Spontanspel utanför en anläggning — backend (0154301b.m-3): migration 00293_dsocial_m3_casual_matches.sql (casual_match + casual_match_participant), självguardade tenant-lösa rutter under /v1/social/casual-matches (logga, läsa, lista egna, radera), D-RANK:s tenant-lösa dörr med emit-svepet och kvittots ratingutfall enbart för anroparen, beskedet social_casual_match_logged genom D-COMM, seed-social-deltat, GDPR-raderingen, kontraktet specs/api/endpoints/social-casual-matches.md och kunskapsguiden social-casual-play (sv + en).

    0154301b.m-3.t-1
  • F15.14.11 Shipped

    Individ-hemmet GET /v1/me/social-home (0154301b.m-3) — EN läsning som komponerar de egna blocken och grannarnas referensblock, målraden och CTA-trappan som SERVERNS härledning, med säker degradering när ett grannblock inte kan läsas.

    0154301b.m-3.t-2
  • F15.14.12 Shipped

    Appytan /spontanspel och hemblocket *Spela* (0154301b.m-3) — *Logga ett spel*, kvittot och *Din spelhistorik* på /spontanspel (utloggad: egen inloggningsuppmaning, djuplänken ?spel=<id> överlever), hemblocket app-hem-spela på / med målraden och CTA-trappan ur social-home, Spela-hubbens kort *Spontanspel — logga ett spel* och tävlingsdetaljens *Söker du medspelare?*; det mockupen ritar som m-3 inte bygger står som synliga ⊘ med ägare. Vy-specar: specs/app/views/spontanspel.md, specs/app/views/spela-hem.md. Bevis: tools/gate/dsocial-m3-e2e.mjs.

    0154301b.m-3.t-3
  • F15.14.13 Planned

    Grupperna (0154301b.m-4, migration 00294_dsocial_m4_groups.sql): montering på valfri nod ELLER direkt under förbundet (flera grupper per nod), skapandet bakom social:groups_manage, medlemskap för vem som helst med en identitet (ej licens-gateat), join_policy open/request med ansökningar, roll-matrisen member/moderator/admin (sista admin skyddad i databasen), synligheten members_only/public/officers_only prövad i frågan (404 utanför), arkivering. Kontrakt: social-groups.md.

    0154301b.m-4.t-1
  • F15.14.14 Planned

    Väggen — inlägg med rubrik och foton (D-SOCIAL:s eget register group_media_blob bakom blob-porten), tråd djup 0..2 (422 comment_depth_exceeded), sluten emoji-mängd, @-omnämnanden av medlemmar, dölja/pinna, SSE-strömmen /v1/social/groups/{id}/stream, åldersgrinden (403 minor_not_permitted, fail-closed).

    0154301b.m-4.t-1
  • F15.14.15 Planned

    Beskeden, publik preview och sys-insyn — fyra social_group_*-typer (fan-out genom jobbet group_post_fanout), den anonyma CMS-previewn /v1/public/social/groups/posts (inga författare/foton), operatörens LÄS-ONLY insyn (sys.social.oversee, innehåll kräver motivering + WORM).

    0154301b.m-4.t-1
  • F15.14.16 Planned

    GDPR, hygien och fixtur — erasure med kvarstående tråd, group_content_retention_tick, personal_data-svepet, hemblocket groups, gruppkamrater i kontaktmängden, seed-deltat i seed-social och guiden social-groups.

    0154301b.m-4.t-1
  • F15.14.17 Planned

    Appytan /gemenskap (fliken *Grupper* levande; *Flöde*, *Forum*, *Recensioner* ⊘ med ägare) och hemmets rad *Dina grupper*.

    0154301b.m-4.t-3