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

Platform Operations & Cross-Cutting Concerns

258 features · 25 subsystems

Cross-cutting capabilities that affect the entire platform: offline support, print integration, localization beyond language, and operational resilience. These are critical for real-world adoption by federations operating in varied conditions.

Offline & Resilience

F16.01
Planned

**Status:** F16.01.01–04 implemented (PL-F1601a), F16.01.05–08 implemented (PL-F1601b)

How it works
  • F16.01.01 Shipped

    Offline tournament management (draw, score entry, standings — sync when online)

    ✅ PL-F1601a
  • F16.01.02 Shipped

    Offline license verification (cached license data on device)

    ✅ PL-F1601a
  • F16.01.03 Shipped

    Conflict-free data sync (CRDT or last-write-wins with conflict UI)

    ✅ PL-F1601a
  • F16.01.04 Shipped

    Offline check-in at events (QR code scan against cached participant list)

    ✅ PL-F1601a
  • F16.01.05 Shipped

    Background sync with retry and queue management

    ✅ PL-F1601b
  • F16.01.06 Shipped

    Sync status indicator (what's pending, what's synced)

    ✅ PL-F1601b
  • F16.01.07 Shipped

    Graceful degradation (features that work offline vs. require connectivity)

    ✅ PL-F1601b
  • F16.01.08 Shipped

    Manual fallback procedures (paper score sheets that can be digitized later)

    ✅ PL-F1601b

Print & Paper Integration

F16.02
Planned

**Status:** F16.02.01–05 implemented (PL-F1602a), F16.02.06–09 implemented (PL-F1602b)

How it works
  • F16.02.01 Shipped

    Printable draw sheets (formatted for posting at venue)

    ✅ PL-F1602a
  • F16.02.02 Shipped

    Printable bracket posters (knockout/elimination tournaments)

    ✅ PL-F1602a
  • F16.02.03 Shipped

    Printable score sheets (per match, for manual scoring)

    ✅ PL-F1602a
  • F16.02.04 Shipped

    Printable court maps (venue layout with court numbers)

    ✅ PL-F1602a
  • F16.02.05 Shipped

    QR codes on printed documents linking to digital records

    ✅ PL-F1602a
  • F16.02.06 Shipped

    OCR/photo scan of paper score sheets for digital entry

    ✅ PL-F1602b
  • F16.02.07 Shipped

    Printable license cards and certificates

    ✅ PL-F1602b
  • F16.02.08 Shipped

    Printable competition regulations and rules summary

    ✅ PL-F1602b
  • F16.02.09 Shipped

    PDF report generation (results, rankings, financial reports)

    ✅ PL-F1602b

Localization & Federation Customization

F16.03
In progress

**Status:** F16.03.01–F16.03.05 implemented (PL-F1603a), F16.03.11+F16.03.13–F16.03.14 implemented (PL-F1603c), F16.03.16–F16.03.20 implemented (PL-F1603d). i18n-utvidgningar: gettext (.po), react-intl, RTL-config, locale-detektering, datumformatering, tyska översättningar (PL-F1607a). Franska/spanska/italienska översättningar, översättnings-CMS, lazy-loading, pluralisering (PL-F1607b).

How it works
  • F16.03.01 Shipped

    Configurable competition class registry — each tenant defines its own age/gender classes with: code, name, type (age/gender/open), min/max age, gender filter, ranking eligibility. Examples: Sweden (M/YJ/J/Ö/D/V55/V65), France (Poussin/Benjamin/Minime/Cadet/Junior/Sénior/Vétéran), Spain (Benjamín→Veterano+Sub-23). Implemented (PL-F1603a): PlayerClass model med class_type-fält (age/gender/open), CompetitionClass = PlayerClass + GameFormat. CRUD-endpoints med unik kodvalidering per tenant. Rebuild — LIVE (D-FEDCONFIG d0601eb6.m-1): Federationskonfigurations-hubben i admin (/federationskonfiguration, Plattform-gruppen, gate tenant-config.manage) redigerar klassregistret som data i tenant_configs.competition_classes via GET/PUT /v1/tenant/config (If-Match/412-flöde, dubblettkod-422, pensionering med retired-flagga — aldrig radering, FR-preset Poussin→Vétéran som data, tomstate). Skapa-tävling-wizarden speglar ändringarna utan omdeploy. Vy-spec: specs/admin/views/federationskonfiguration.md.

    ✅ PL-F1603a
  • F16.03.02 Shipped

    Configurable game format registry — each tenant defines: code, name, players per team, boules per player, mixed gender required. Standard formats (s/d/t/Mxd/Mxt) pre-seeded, tenants can add variants. Implemented (PL-F1603a): GameFormat model med mixed_gender_required. CRUD-endpoints med unik kodvalidering per tenant. Rebuild — LIVE (D-FEDCONFIG d0601eb6.m-1): spelform-katalogen (tenant_configs.game_formats: kod, family, etikett, aktiv per disciplin, default-klot/maxpoäng) redigeras i federationskonfigurations-hubben; active: false/disciplin-scopning filtrerar wizardens formatval (gameFormatsForDiscipline).

    ✅ PL-F1603a
  • F16.03.03 Shipped

    Configurable competition level registry — each tenant defines its levels with: code, name, who can sanction, ranking eligibility, ranking coefficient, required referee grade, venue requirements, sanction deadline, quota. Not hardcoded. Implemented (PL-F1603a): CompetitionLevel med sanction_deadline_days och quota. CRUD-endpoints med unik kodvalidering per tenant.

    ✅ PL-F1603a
  • F16.03.04 Shipped

    Configurable fee structure with OrgNode-level components — license fee can be split across multiple OrgNode levels (federation + region + district + insurance). Each level sets its own component. Supports regional price variation. Implemented (PL-F1603a): LicenseFeeConfig med FeeComponent[], LicenseFeeOverride per OrgNode, beräkningsendpoint som resolver OrgNode-hierarkin.

    ✅ PL-F1603a
  • F16.03.05 Shipped

    Configurable license type registry — each tenant defines its license types with: code, name, competition eligibility, insurance inclusion, age limits, duration (annual/time-limited), medical certificate requirement, renewability. Implemented (PL-F1603a): LicenseType med alla fält inkl. renewable, includes_insurance, base_fee, fee_currency, requires_parental_consent, reduced_fee_pct. Fullständig validering.

    ✅ PL-F1603a
  • F16.03.06 Shipped

    Configurable transfer rules — per tenant: transfer window dates, max transfers per season, required approvals (player/old club/new club/federation/district), cross-region extra approval, outstanding debt blocking, quarantine during league. Implemented (PL-F1603b): Dedikerade GET/PUT-endpoints under /tenant-config/transfer-config/. Full TransferConfig med alla fält inkl. ITC-inställningar.

    ✅ PL-F1603b
  • F16.03.07 Shipped

    Configurable ranking algorithm — per tenant: point scale per level × placement × participant count, validity period (1/2/3 years), weighting across years (Germany: 3×/2×/1×), max competitions counted, separate per class/format/gender. Implemented (PL-F1603b): RankingAlgorithmConfig i TenantConfig med ParticipantThreshold-stöd. Dedikerade GET/PUT-endpoints under /tenant-config/ranking-algorithm/.

    ✅ PL-F1603b
  • F16.03.08 Shipped

    Configurable OrgNode hierarchy — per tenant: number of levels (0-3+), type names per level, capabilities per level (can_sanction, can_set_license_price, has_discipline_body, can_issue_licenses). Implemented (PL-F1603b): Dedikerade GET/PUT-endpoints under /tenant-config/org-hierarchy/ med validering av djup och sekventiella nivåer.

    ✅ PL-F1603b
  • F16.03.09 Shipped

    Configurable umpire grade registry — per tenant: grade names, count, progression path, playing-umpire allowed per level. Implemented (PL-F0501a, verifierat PL-F1603b): Befintliga endpoints under /tenant-config/umpire-grades/ med full progression, multi-part exam och recertification.

    ✅ PL-F1603b
  • F16.03.10 Shipped

    Configurable discipline rules — per tenant: penalty amounts, suspension rules, who can decide (only federation vs also district). Implemented (PL-F1603b): Utökad DisciplinaryConfigSchema med sanction_catalog (straffkatalog med belopp, typ, varaktighet) och adr_config (alternativ tvistlösning). Beslutsmyndighet konfigureras via levels[].org_node_level.

    ✅ PL-F1603b
  • F16.03.11 Shipped

    Configurable medical requirements — per tenant: medical certificate required (yes/no), at first license only, renewal cycle, attestation sur l'honneur, health certificate for international play. Implemented (PL-F1603c): MedicalCertificateConfig med required_at_first_license_only och health_certificate_for_international. Dedikerad config-endpoint GET /medical-certificates/config.

    ✅ PL-F1603c
  • F16.03.12 Shipped

    Multi-language UI — architected for 43+ FIPJP-member-nation languages, with 5 launch languages delivered (en, sv, fr, es, it) and additional languages activated per nation via tools/translate/translate.py. Implemented (PL-1701, refined under PL-T328): i18n framework, language picker, per-tenant default language, translation management API, completeness reporting (CI gate i18n-coverage enforces ≥ 80% Tier-1 coverage). See specs/shared/i18n.md and docs/engineering/i18n-strategy.md. Rebuild — LIVE i admin-chromet (D-NAV2 491daab6.m-3, FR-5): skalets och ⌘K-palettens chrome (inkl. palettmotorns arg-felkoder, som numera är stabila KODER översatta vid rendering) resolveras genom den delade i18n-runtimen över microcopy-nycklarna shell.*/palette.*; språkväljaren sitter i skalets languagePicker-slot och byter språk i ett re-render-svep utan omladdning, valet persisterar i @petanque-life/shell.locale och html lang följer localen (WCAG 3.1.1). Se specs/admin/orientering-och-chrome-sprak.md §4.

    ✅ PL-F1603c
  • F16.03.13 Shipped

    Multi-language content management (rules, news, documents). Implemented (PL-F1603c): LocalizedContent modell med per-språk title/body/summary, CRUD-endpoints under /content, slug-lookup, per-language translation management (PUT/DELETE /content/{id}/translations/{lang}).

    ✅ PL-F1603c
  • F16.03.14 Shipped

    Locale-aware formatting (dates, numbers, currency). Implemented (PL-F1603c): LocaleFormattingConfig i TenantConfig med date/time/number/currency-format. Endpoints GET /i18n/locale-formats/{locale} och GET /i18n/locale-formats. Inbyggda defaults för en/sv/fr/de/es/ar.

    ✅ PL-F1603c
  • F16.03.15 Shipped

    Right-to-left language support. Implemented (PL-1701): RTL detection and layout mirroring for Arabic, Hebrew, Persian, Urdu via I18nManager (React Native) and dir="rtl" (web).

    ✅ PL-F1603c
  • F16.03.16 Shipped

    Tenant-level branding — logo, colors, terminology per federation. Implemented (PL-F1603d): BrandingConfig embedded i TenantConfig med logo_url, logo_dark_url, favicon_url, primary/secondary/accent/background/text colors (hex-validerade), font_heading, font_body, custom_css. Dedikerade endpoints GET/PUT /tenant-config/branding/.

    ✅ PL-F1603d
  • F16.03.17 Shipped

    Federation-specific terminology mapping — each tenant maps generic terms to local equivalents. Implemented (PL-F1603d): STANDARD_TERMINOLOGY_KEYS (26 standardtermer), dedikerade endpoints GET/PUT /tenant-config/terminology/ som returnerar terminology dict + standard_keys lista.

    ✅ PL-F1603d
  • F16.03.18 Shipped

    Game variant support — Jeu Provençal, Pétanque, Sport-Boules. Implemented (PL-F1603d): GameVariantConfig med variant, enabled, name i18n, court_dimensions (CourtDimensions), boules_per_player, max_points, team size, competition_class_codes, rules_document_url. Dedikerade endpoints GET/PUT /tenant-config/game-variants/ med unik variant-validering. Rebuild — LIVE (D-FEDCONFIG d0601eb6.m-1): disciplinerna (inkl. sport_boules) växlas per tenant i federationskonfigurations-hubben (disciplines []string) — startvärdet är referenskatalogens default-aktiva mängd (de tre FIPJP-erkända), skrivet av C-1:s tenant-defaults (2c5f5552.m-2.t-1); variantfälten (banmått, klot, maxpoäng, lagformer, tillåtna klasser, regeldokument-URL) bor i tenant_configs.discipline_settings och round-trippar via PUT /v1/tenant/config.

    ✅ PL-F1603d
  • F16.03.19 Shipped

    Variable OrgNode depth per branch within tenant — non-uniform hierarchy depth. Implemented (PL-F1603d): BranchHierarchyOverride med region_code, region_name i18n, extra_levels (list[OrgNodeTypeConfig]), description. Dedikerade endpoints GET/PUT /tenant-config/branch-hierarchy/ med unik region_code-validering.

    ✅ PL-F1603d
  • F16.03.20 Shipped

    Delegated admin and CMS per OrgNode level — autonomous sub-federations. Implemented (PL-F1603d): has_own_admin och has_own_cms boolean-fält i OrgNodeTypeConfig. Dedikerad read-endpoint GET /tenant-config/delegated-admin/ som returnerar delegated_levels + parent_branding.

    ✅ PL-F1603d
  • F16.03.21 Shipped

    Federationspolicies för nya tävlingar + ranking-koppling — LIVE (D-FEDCONFIG d0601eb6.m-3): förbundets policy-standarder redigeras som ren data i tenant_configs.competition_policies (jsonb {entry_default, protest_jury_default, scoring_default}; struct-validerad, seedad scoring 13, minimal-PUT via PUT /v1/tenant/config med If-Match/412 över den delade versionslinjen) i federationskonfigurations-hubbens #policies-flik: anmälningsdefault (avgift i huvudenhet ↔ heltal minor + valuta, betalning vid anmälan, licenskrav, väntelista, max lag), protest-/jurydefault (jurystorlek, protestavgift) och poäng-standard (avslutsvillkor med "runda"-terminologi, 13/11/Annat…, tidsgräns, sista runda). Semantik: default, inte tvång — skapa-tävling-wizarden FÖRFYLLS ur policyn (poäng-triaden, anmälan-steget, jurystorleken; tävlingen föds med federationens default_scoring explicit i create-anropet) och varje tävling kan avvika; osatta delar märks ärligt som plattformsdefault. #ranking-koppling-fliken är en läsöversikt (klassernas counts_toward_ranking + nivåernas ranking_eligible ur befintlig katalogdata, länk till D-RANK-konfigen) — ingen dubblerad redigering; systerytorna övergångsregler/licensregler/rankingalgoritm länkas, byggs inte om. Initiativets DoD-mening bevisad och mekaniskt vaktad: *wizarden läser ENBART katalogerna (inga hårdkodade val kvar)* — tools/gate/guards.d/wizard-catalog-guard.mjs körs ovillkorligt via tools/gate/guards-run.mjs (verify.sh L3c, konventionsbaserad guards-discovery sedan D-MODULAR d2fe934e.m-10) och står röd om ett hårdkodat katalog-/policyval återinförs; runtime-svepet tools/gate/fedconfig-m3-e2e.mjs (verify.sh L4, deskriptor tools/gate/e2e.d/050-fedconfig-m3.mjs) bevisar policy-CRUD, 412-flödet, wizard-genomslaget (poäng/entry/jury + skapad tävlings kolumner via API) och ranking-vyn mot körande stack. Vy-spec: specs/admin/views/federationskonfiguration.md + specs/admin/views/competition-wizard.md §Policy-förfyllnaden; API-delta: specs/api/endpoints/tenant-config.md.

    ✅ d0601eb6.m-3.t-2

Data Visibility & Public Access

F16.04
In progress

**Status:** F16.04.01–04 implemented (PL-F1604a), F16.04.05–08 implemented (PL-F1604b)

How it works
  • F16.04.01 Shipped

    Public user role (unauthenticated access to public data) — spectator roll, @public decorator, metadata-endpoint

    ✅ PL-F1604a
  • F16.04.02 Shipped

    Per-resource public field configuration — PublicFieldConfig modell, admin CRUD, defaults, effektiv vy

    ✅ PL-F1604a
  • F16.04.03 Shipped

    Global public data: world rankings (scope_type=world), federation directory, rules (GET /public/rules)

    ✅ PL-F1604a
  • F16.04.04 Shipped

    National public data: national rankings (scope_type=national), results, club/district directory (federation_code-filter)

    ✅ PL-F1604a
  • F16.04.05 Shipped

    Player privacy settings (what's visible on public profile) — PrivacySettings embedded i PlayerProfile, GET/PUT /player-profiles/{id}/privacy-settings, GET /public/players/ respekterar profile_visible+visibility, GET /public/players/{id} respekterar show_club/show_date_of_birth/show_ranking/show_results, hidden profiles returnerar 404

    ✅ PL-F1604b
  • F16.04.06 Shipped

    Capability-based field visibility (sensitive fields hidden by role) — GET /player-profiles/{id}/visible-fields med tre-tier-filter (public/member/sensitive via apply_role_filter), SensitiveFieldRule med required_capability (profile:read_dob, profile:read_license_number, profile:read_contact), tenant-specifika PublicFieldConfig-overrides

    ✅ PL-F1604b
  • F16.04.07 Shipped

    Public API for rankings, results, calendar (open data) — GET /public/open-data API-katalog med 14 endpoints, GET /public/calendar med federation_code-filter, GET /public/competitions med federation_code-filter, GET /public/calendar/ical RFC 5545 iCalendar-feed, cursor-paginering på alla listendpoints

    ✅ PL-F1604b
  • F16.04.08 Shipped

    SEO-friendly public pages (for discoverability) — GET /public/seo/sitemap.xml XML-sitemap med federation/club/competition/venue/ranking-sidor, JSON-LD structured data för federation (SportsOrganization), competition (SportsEvent), calendar-event (Event), venue (SportsActivityLocation), Open Graph metadata GET /public/seo/meta/{resource_type}/{id} för federation/club/competition/venue/calendar_event

    ✅ PL-F1604b

Event Day Contingency

F16.05
Planned

**Status (Go-rebuild):** F16.05.01/.03/.04/.05/.06/.07 rebuild-DONE i **D-COMP-OPS m-6.3**

How it works
  • F16.05.01 Shipped

    Weather-triggered tournament pause/cancellation workflow (avbrotts-state-machine + bindande domar-/jury-grind)

    ✅ D-COMP-OPS m-6.3
  • F16.05.02 Shipped

    Participant no-show management with automatic draw adjustment

    ✅ D-COMP-OPS m-3 (Table Official-desk)
  • F16.05.03 Shipped

    Court unavailability handling (omfördelnings-FÖRSLAG — accept/reject, ingen auto-flytt)

    ✅ D-COMP-OPS m-6.3
  • F16.05.04 Shipped

    Partial results handling (räknar via scoring/standings mot tenant-minimum: counted/annulled)

    ✅ D-COMP-OPS m-6.3
  • F16.05.05 Shipped

    Refund/credit processing for cancelled events (batch via befintliga PAY-refund-vägen, idempotent)

    ✅ D-COMP-OPS m-6.3
  • F16.05.06 Shipped

    Tournament continuation from saved state (resume next day; snapshot active→restored)

    ✅ D-COMP-OPS m-6.3
  • F16.05.07 Shipped

    Emergency communication to all participants (verklig mottagarupplösning + in-app + kvittering; extern transport = dispatch-port, spy=0)

    ✅ D-COMP-OPS m-6.3

Federation Reporting & Compliance

F16.06
Planned

**Status:** Implemented (PL-1706), buggfixar och integrationstester (PL-F1606a), sponsorrapportering, revisionsklar data, compliance-checklista automation och GDPR-dataläckenotifiering (PL-F1606b)

How it works
  • F16.06.01 Shipped

    Mandatory report templates per federation level — ReportTemplate with configurable sections, deadlines, frequency

    ✅ PL-F1606a
  • F16.06.02 Shipped

    Automated report generation from system data — GeneratedReport with typed KPI sections (membership, competitions, financial, umpires, sponsors)

    ✅ PL-F1606a
  • F16.06.03 Shipped

    Report submission deadline tracking and reminders — ReportDeadline with reminder/escalation workflow

    ✅ PL-F1606a
  • F16.06.04 Shipped

    Upward reporting (club > nation > continent > FIPJP) — Aggregation endpoint combines child reports into parent

    ✅ PL-F1606a
  • F16.06.05 Shipped

    Government/sports council report generation — GovernmentReportSection with Riksidrottsförbundet-compatible fields

    ✅ PL-F1606a
  • F16.06.06 Shipped

    Sponsor reporting (participation data, exposure metrics) — SponsorSection with impressions, clicks, value

    ✅ PL-F1606b
  • F16.06.07 Shipped

    Audit-ready data with full trail — is_audit_ready flag on reports, revision tracking on all documents

    ✅ PL-F1606b
  • F16.06.08 Shipped

    Compliance checklist automation (tournament had jury, insurance, etc.) — ComplianceChecklist with auto-scoring and action items

    ✅ PL-F1606b
  • F16.06.09 Shipped

    Data breach notification workflow (GDPR Article 33/34) — DataBreachNotification with 72h tracking and full timeline

    ✅ PL-F1606b

Stakeholder Feedback & Feature Requests

F16.07
Planned

**Status:** Implemented (PL-2110)

How it works
  • F16.07.01 Shipped

    Structured feature request form with 6 sections (contact, stakeholder category, request details, acceptance criteria, attachments, priority)

    ✅ PL-F1607a
  • F16.07.02 Shipped

    Live Markdown preview — real-time rendering of generated task-spec alongside the form

    ✅ PL-F1607a
  • F16.07.03 Shipped

    Stakeholder category/subcategory selection — 11 categories with dynamic subcategory filtering

    ✅ PL-F1607a
  • F16.07.04 Shipped

    Feature inventory integration — searchable dropdown over 1 250+ features for "related feature" linking

    ✅ PL-F1607a
  • F16.07.05 Shipped

    Markdown task-spec generation — structured .md file following backlog runner format

    ✅ PL-F1607a
  • F16.07.06 Shipped

    Email delivery via Azure Communication Services — support notification + stakeholder confirmation

    ✅ PL-F1607a
  • F16.07.07 Shipped

    Persistent storage with audit trail — feature_requests collection with revision tracking and soft-delete

    ✅ PL-F1607b
  • F16.07.08 Shipped

    Admin triage endpoints — list, detail, status update, soft-delete via REST API

    ✅ PL-F1607b
  • F16.07.09 Shipped

    Anti-spam protection — honeypot field, Cloudflare Turnstile CAPTCHA, rate limiting (5/h per IP)

    ✅ PL-F1607b
  • F16.07.10 Shipped

    i18n — form available in EN, FR, ES, SV; generated Markdown always in English

    ✅ PL-F1607b
  • F16.07.11 Shipped

    WCAG 2.1 AA accessibility — labelled fields, aria attributes, keyboard navigation, contrast compliance

    ✅ PL-F1607b
  • F16.07.12 Shipped

    SEO — per-language meta tags, JSON-LD ContactPage schema, sitemap inclusion

    ✅ PL-F1607b
  • F16.08.01 Shipped

    Score entry offline med WatermelonDB — WatermelonDB-compatible pull/push sync protocol with delta timestamps, per-table change tracking (match_scores, mene_scores), idempotent push with version-based conflict detection

    ✅ PL-F1608a
  • F16.08.02 Shipped

    License-verifiering med cachad data — HMAC-SHA256 integrity hash on license bundles, incremental delta download via previous_hash, server-side license verification endpoint

    ✅ PL-F1608a
  • F16.08.03 Shipped

    Sync-status indikator — sync session creation/completion, per-type progress tracking with percentage, pending/completed/failed/conflict counts, enhanced UI progress bar and per-type breakdown

    ✅ PL-F1608a
  • F16.08.04 Shipped

    Konflikthantering vid sync (last-write-wins) — configurable per-resource-type policies with auto/manual field classification, LWW/server-wins/client-wins/manual strategies, full audit trail via ConflictAuditEntry

    ✅ PL-F1608a
  • F16.08.05 Shipped

    Bakgrundssync vid wifi-anslutning — per-user wifi-only preference (BackgroundSyncPreference), auto-sync on reconnect, periodic sync interval, quiet hours, sync trigger evaluation based on connection type and preferences

    ✅ PL-F1608b
  • F16.08.06 Shipped

    Offline-fönster max 7 dagar — configurable max offline window (default 7 days), 12h grace period, 5-day warning threshold, per-data-type validation, automatic invalidation of expired offline data

    ✅ PL-F1608b
  • F16.08.07 Shipped

    Selective sync (bara aktuell tävling) — per-user competition scope (SelectiveSyncScope), sync only selected competitions, estimated data size calculation, automatic inclusion of requested competition in scope

    ✅ PL-F1608b
  • F16.08.08 Shipped

    Bandwidth-medveten sync (3G vs wifi) — per-connection-type profiles (wifi/5G/4G/3G/2G/ethernet), adaptive batch sizing based on effective bandwidth and signal strength, image sync control, compression, throttling

    ✅ PL-F1608b

Audit Log & Compliance Retention

F16.09
Planned

**Status:** F16.09.01–04 implemented (PL-F1609a), F16.09.05–08 implemented (PL-F1609b)

How it works
  • F16.09.01 Shipped

    Strukturerad audit log via Craft Easy — unified cross-domain AuditLog collection with 13 domain categories, central write service (write_audit_log), read endpoints (GET /audit-log/, entity trail, actor trail), append-only immutable contract. Implemented (PL-F1609a): AuditLog model med domain/action/entity_type/entity_id/actor_id/details/search_text, 7 composite indexes, tenant-scoped.

    ✅ PL-F1609a
  • F16.09.02 Shipped

    Utökad loggning för disciplin-ärenden — extended DisciplinaryAuditEntry med ip_address, user_agent, entity_type, DisciplinaryAuditAction enum (30 actions: case lifecycle, hearing, sanction, appeal, ADR, CAS), dual-write till central audit log via write_discipline_audit. Implemented (PL-F1609a).

    ✅ PL-F1609a
  • F16.09.03 Shipped

    Utökad loggning för ekonomi-mutationer — write_finance_audit service som skriver till både FinancialAuditLog och central AuditLog, enriched details med amount/currency, coverage för alla 80+ FinancialAuditAction typer. Implemented (PL-F1609a).

    ✅ PL-F1609a
  • F16.09.04 Shipped

    Audit log search med fritext — search_text fält med auto-genererad sökbar text, regex-baserad case-insensitive sökning (PostgreSQL-kompatibel), GET /audit-log/search?q= endpoint med domain/datum-filter, paginering. Implemented (PL-F1609a).

    ✅ PL-F1609a
  • F16.09.05 Shipped

    Obegränsad audit-retention — AUDIT_LOG_RETENTION_DAYS=0 (indefinite), validate_retention_policy service flaggar non-compliant tenant-policies, GET /audit-log/retention endpoint. Inga TTL-indexes, inga expireAfterSeconds. Rättslig grund: GDPR Art. 6(1)(f). Implemented (PL-F1609b).

    ✅ PL-F1609b
  • F16.09.06 Shipped

    GDPR-dataexport inkluderar audit logs — export_audit_logs_for_user service samlar alla entries där användaren är actor, integrerat i GET /me/data-export (Art. 20). Implemented (PL-F1609b). 🔁 Go-rebuild 742fe3db.m-2 (D-GDPR m-2): DSAR-arkivets kategori access_log bär data_access_audit-raderna DÄR PERSONEN ÄR SUBJEKT (vem som läst hens uppgifter, Art. 15-transparensen) — aktörens ip_address/user_agent ingår aldrig, de hör till privacy-officerns utredningsprojektion. Sys-konsolens GDPR-snapshot (t-3) bär motsvarande kategori audit_actions_on_user: audit_log-rader riktade MOT personen, utan diff (ostrukturerat fält ⇒ utanför allowlisten), maxad till de senaste 200.

    ✅ PL-F1609b
  • F16.09.07 Shipped

    GDPR right-to-erasure (anonymisering) — anonymise_audit_logs_for_user ersätter PII (actor_id→null, ip→0.0.0.0, UA→ANONYMISED, PII-nycklar i details→ANONYMISED) utan radering. is_anonymised-flagga. Integrerat i POST /data-deletion-requests/{id}/process. Implemented (PL-F1609b).

    ✅ PL-F1609b
  • F16.09.08 Shipped

    Per-fält PII-klassificering — PIICategory enum (identity/network/behavioural/contextual), json_schema_extra PII-taggar, AuditLog.pii_fields() classmethod, ANONYMISATION_RULES, GET /audit-log/pii-schema endpoint. Implemented (PL-F1609b).

    ✅ PL-F1609b
  • F16.09.09 Planned

    Per-tenant retention-policy + retention-drift (Go-rebuild 742fe3db.m-3, D-GDPR m-3) — data_retention_policy (migration 00128) med regler per kategori (tolv validerade kategorier, retention_days 0 = obegränsad, art. 6-grund) och högst en aktiv policy per förbund som databasinvariant. CRUD GET/POST /v1/data-retention-policies + PATCH/DELETE /{id} (capability gdpr:manage, If-Match, soft-delete); 422 retention_rule_invalid och 422 retention_below_statutory_minimum (accounting 2555 d, gdpr_notifications 1825 d enligt art. 33(5)). whistleblower_records har INGEN fast tid — EU 2019/1937 sätter ingen, så kategorin är per-tenant/land-konfigurerbar med subcategory ∈ {investigated, not_investigated}; ett källkodstest bevisar att ingen kodväg bär en femårsgräns. Retention-svepet gdpr_retention_sweep gallrar via SAMMA RetentionGuard-väg som Art. 17-raderingen (två registrerade källor: consent_record, profile_photo). Sys-driftvyn GET /v1/sys/compliance/retention (capability sys.support.lookup) + sys/app/efterlevnad/retention visar konfigurerad TTL mot äldsta observerade rad per förbund och kategori, med avvikelse-badge som bär ordet "Avvikelse" och lägena upprätthålls/observeras/saknas — en kategori utan källa redovisas ärligt som *saknas*, aldrig som grön. Se specs/sys/views/compliance-retention.md + specs/api/endpoints/retention-guard.md.

    ♻️ 742fe3db.m-3

Rate Limiting & DoS Protection

F16.10
Planned

**Status:** F16.10.01–04 implemented (PL-F1610a), F16.10.05–07 implemented (PL-F1610b)

How it works
  • F16.10.01 Shipped

    Rate limiting per IP — sliding-window per-IP enforcement via RateLimitMiddleware, configurable limit/window (default 200/60s), 429 response with Retry-After and X-RateLimit-* headers, health-check paths exempt. Implemented (PL-F1610a).

    ✅ PL-F1610a
  • F16.10.02 Shipped

    Rate limiting per user — per-authenticated-user sliding window (default 300/60s), keyed on principal_id from auth context, public pseudo-users excluded, independent from per-IP limits. Implemented (PL-F1610a).

    ✅ PL-F1610a
  • F16.10.03 Shipped

    Rate limiting per tenant — aggregate sliding window across all users within a tenant (default 5000/60s), keyed on tenant_id from auth context, prevents single-tenant resource monopolisation. Implemented (PL-F1610a).

    ✅ PL-F1610a
  • F16.10.04 Shipped

    Cloudflare DDoS-skydd — CloudflareMiddleware extracts real client IP from CF-Connecting-IP, passes through CF-Ray/CF-IPCountry as response headers, optional origin protection (CLOUDFLARE_REQUIRE_HEADER) blocks direct access (403), X-Cloudflare-Protected response header. Implemented (PL-F1610a).

    ✅ PL-F1610a
  • F16.10.05 Shipped

    Brute-force-skydd på login — LoginProtectionMiddleware monitors auth endpoints (POST /auth/token, /auth/login, etc.) for failed login attempts (HTTP 401/403), applies per-IP exponential backoff (1s→2s→4s→...→30s capped), lockout with Retry-After header. Implemented (PL-F1610b).

    ✅ PL-F1610b
  • F16.10.06 Shipped

    CAPTCHA fallback vid attack — after configurable failure threshold (default 3), requires Cloudflare Turnstile CAPTCHA token via X-Captcha-Token header before allowing further login attempts. Returns 429 with captcha_required: true. Shared verify_turnstile_token service. Implemented (PL-F1610b).

    ✅ PL-F1610b
  • F16.10.07 Shipped

    Adaptive rate limits baserat på trafik — AdaptiveRateLimitController monitors total API traffic volume in sliding window, dynamically adjusts rate limits across three levels: normal (1.0×), elevated (0.5× at 2× baseline), critical (0.25× at 5× baseline). Integrated with RateLimitMiddleware. Implemented (PL-F1610b).

    ✅ PL-F1610b
  • F16.10.08 Shipped

    Turnstile bot-protection på alla publika POST-endpoints — TurnstileMiddleware för auth-endpoints + Depends(verify_turnstile) för marketing/signup/contract/feature-requests. Defense-in-depth med honeypot + rate-limit. Shared widget i packages/shared/src/turnstile/. CSP uppdaterad för challenges.cloudflare.com. Fail-open vid CF-timeout (5s). Implemented (PL-T072).

    ✅ PL-T072

Security Headers & CORS

F16.11
Planned

**Status:** F16.11.01–04 implemented (PL-F1611a), F16.11.05–07 implemented (PL-F1611b), F16.11.08 implemented (PL-T287)

How it works
  • F16.11.01 Shipped

    CORS-policy per miljö och app — per-environment CORS origin allowlist med production-origins för admin/app/www/petanque.life + localhost i dev, wildcard-reject i production via model_validator, credentials/methods/headers/expose/max-age konfigurerat. Implemented (PL-F1611a).

    ✅ PL-F1611a
  • F16.11.02 Shipped

    Content-Security-Policy med nonces — per-request secrets.token_urlsafe(16) nonce injicerat i CSP script-src och style-src via {nonce}-placeholder, frame-ancestors 'none', base-uri 'self', form-action 'self', upgrade-insecure-requests. Nonce tillgängligt via request.state.csp_nonce. Implemented (PL-F1611a).

    ✅ PL-F1611a
  • F16.11.03 Shipped

    HSTS preload-list inclusion — Strict-Transport-Security: max-age=31536000; includeSubDomains; preload på alla HTTPS-svar, HSTS-header utelämnad på plain HTTP, proxy-stöd via X-Forwarded-Proto. Implemented (PL-F1611a).

    ✅ PL-F1611a
  • F16.11.04 Shipped

    X-Frame-Options DENY för admin — X-Frame-Options: DENY som default (konfigurerbar via SECURITY_HEADERS_X_FRAME_OPTIONS), förhindrar clickjacking på admin- och API-endpoints. Implemented (PL-F1611a).

    ✅ PL-F1611a
  • F16.11.05 Shipped

    Referrer-Policy strict-origin — konfigurerbar via SECURITY_HEADERS_REFERRER_POLICY, Petanque Life default strict-origin (striktare än the platform default strict-origin-when-cross-origin). Skickar bara origin, aldrig path/query. Implemented (PL-F1611b).

    ✅ PL-F1611b
  • F16.11.06 Shipped

    Permissions-Policy per app-typ — konfigurerbar via SECURITY_HEADERS_PERMISSIONS_POLICY, inaktiverar camera, microphone, geolocation, payment, usb, magnetometer, gyroscope, accelerometer, ambient-light-sensor, autoplay. Tillåter fullscreen, encrypted-media, picture-in-picture för same-origin. Implemented (PL-F1611b).

    ✅ PL-F1611b
  • F16.11.07 Shipped

    Subresource Integrity (SRI) för CDN-assets — SriAsset-modell, hash-beräkning (sha256/384/512), publikt manifest (GET /sri/manifest), admin CRUD, re-verifiering. Implemented (PL-F1611b).

    ✅ PL-F1611b
  • F16.11.08 Shipped

    Security.txt (RFC 9116) — /.well-known/security.txt på alla publika domäner (petanque.life, *.web.petanque.life, api/admin/app/sys/design.petanque.life). Shared TS-generator i packages/shared/src/security/security-txt.ts, API-route i api/src/petanque_api/routes/well_known.py, build-time-genererad fil för Expo SWA via tools/security/generate-security-txt.mjs. PGP-nyckel + disclosure-policy (en/sv/fr/es) + hall-of-fame på petanque.life. Veckovis CI-kontroll av Expires (tools/security/verify_security_txt.py). Implemented (PL-T287).

    ✅ PL-T287

Capability-baserad åtkomstkontroll

F16.12
Planned

**Status:** F16.12.01–06 implemented (PL-F1612)

How it works
  • F16.12.01 Shipped

    Capability-check på varje endpoint — POST /capability-check och POST /capability-check-batch verifierar att anroparen har erforderlig capability. Nekad åtkomst loggas i audit log. Implemented (PL-F1612).

    ✅ PL-F1612
  • F16.12.02 Shipped

    Capability-tilldelning per OrgNode-roll — GET /users/{user_id}/orgnode-capabilities/{org_node_id} beräknar effektiva capabilities för en användare vid en specifik OrgNode, filtrerar på scope (system/tenant/org_node). Implemented (PL-F1612).

    ✅ PL-F1612
  • F16.12.03 Shipped

    Konfigurerbara capabilities per tenant — GET/PUT /tenant-capability-config/{tenant_id} låter varje federation inaktivera capabilities och begränsa till specifika domäner. TenantCapabilityConfig-modell med disabled_capabilities och enabled_domains. Implemented (PL-F1612).

    ✅ PL-F1612
  • F16.12.04 Shipped

    Capability audit log — GET /capability-audit querying av capability-domänens audit-logg. Alla grant/revoke/deny/config-ändringar loggas under AuditLogDomain.CAPABILITY. Implemented (PL-F1612).

    ✅ PL-F1612
  • F16.12.05 Shipped

    Tidsbegränsade capabilities (sommarvikariat) — GET /capability-grants/expiring listar utgående grants, GET /capability-grants/summary ger dashboard-översikt. Stöd för valid_from/valid_until på rolltilldelningar. Implemented (PL-F1612).

    ✅ PL-F1612
  • F16.12.06 Shipped

    Capability-templates per roll-arketyp — POST/GET/PUT/DELETE /capability-templates, POST /capability-templates/{id}/apply. CapabilityTemplate-modell för användardefinierade capability-buntar med wildcard-expansion vid applicering. Implemented (PL-F1612).

    ✅ PL-F1612

Observability och driftövervakning

F16.13
In progress

**Status:** F16.13.01–09 implemented (PL-F1613a, PL-F1613b)

How it works
  • F16.13.01 Shipped

    Strukturerad loggning med structlog — sensitive-field masking (SENSITIVE_FIELDS frozen set), request context binding (request_id/tenant_id/user_id/trace_id), JSON/console renderers, startup info event, ObservabilityMiddleware logs every request with method/path/status/duration/client_ip. Implemented (PL-F1613a).

    ✅ PL-F1613a
  • F16.13.02 Shipped

    Prometheus metrics — the platform's MetricsMiddleware (HTTP requests, duration, in-flight, request/response size), petanque-specific business metrics: petanque_license_operations_total, petanque_auth_operations_total, petanque_tenant_api_requests_total, petanque_match_scores_submitted_total, petanque_audit_log_entries_total, petanque_sync_operations_total, petanque_db_query_duration_seconds, petanque_health_check_*. /metrics scrape endpoint. Implemented (PL-F1613a).

    ✅ PL-F1613a
  • F16.13.03 Shipped

    OpenTelemetry distributed tracing — setup_telemetry() initializes OTLP gRPC exporter, auto-instruments the API, HTTP clients and the data layer, injects trace_id/span_id into structlog events, X-Trace-Id response header, graceful no-op when OTel packages absent. Configurable via OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME. Implemented (PL-F1613a).

    ✅ PL-F1613a
  • F16.13.04 Shipped

    Health check endpoints — the platform's /health (liveness) and /ready (readiness with database ping), petanque /startup (startup probe for Kubernetes/Azure Container Apps), /health/detailed (per-dependency status with latency: database, Redis), /observability/config (non-sensitive config inspection). Implemented (PL-F1613a).

    ✅ PL-F1613a
  • F16.13.05 Shipped

    Grafana dashboards — JSON provisioning under infrastructure/grafana/: dashboard provider config (provisioning/dashboards.yml), API overview dashboard (dashboards/api-overview.json) with 12 panels: request rate, latency p50/p95/p99, in-flight, error rate, health status, health latency, license ops, auth ops, tenant traffic, DB query latency, audit log rate, sync ops/conflicts. Implemented (PL-F1613a).

    ✅ PL-F1613a
  • F16.13.06 Shipped

    Alert routing till PagerDuty/Opsgenie — per-tenant alert routing config (AlertRoutingConfig), stöd för PagerDuty Events API v2, Opsgenie Alerts API v2, generiska webhooks, cooldown-baserad deduplicering, AlertHistory audit trail. Endpoints: GET/PUT /observability/alerts/config/{tenant_id}, POST /observability/alerts/fire/{tenant_id}, GET /observability/alerts/history/{tenant_id}. Prometheus-metrics: petanque_alerts_fired_total, petanque_alerts_suppressed_total. Implemented (PL-F1613b).

    ✅ PL-F1613b
  • F16.13.07 Shipped

    Per-tenant SLO-mätning — konfigurerbara SLO-mål per tenant (TenantSLOConfig) med systemdefaulter (99.9% tillgänglighet, p95 ≤ 300 ms, p99 ≤ 800 ms). SLOMeasurement dokument för historik. Endpoints: GET/PUT /observability/slo/config/{tenant_id}, POST /observability/slo/measure/{tenant_id}, GET /observability/slo/status/{tenant_id}, GET /observability/slo/history/{tenant_id}/{slo_type}. Prometheus-gauge: petanque_slo_compliance_ratio. Implemented (PL-F1613b).

    ✅ PL-F1613b
  • F16.13.08 Shipped

    Error budget burndown — beräknar felbudget från SLO-mål och faktisk compliance, burnrate per timme, estimerad tid till budgetuttömning. Status: ok/warning/critical/exhausted. Endpoint: GET /observability/slo/burndown/{tenant_id}. Prometheus-gauge: petanque_error_budget_remaining_ratio. Implemented (PL-F1613b).

    ✅ PL-F1613b
  • F16.13.09 Shipped

    Sentry för frontend-felfångst — Sentry DSN-konfiguration för frontend-appar (GET /observability/sentry/config), envelope tunnel för CSP-compliance (POST /observability/sentry/envelope), lättviktig felrapportering (POST /observability/sentry/report). Prometheus-counter: petanque_frontend_errors_total. Implemented (PL-F1613b).

    ✅ PL-F1613b
  • F16.13.10 Shipped

    Live status page — publik statussida som hämtar realtidsstatus via GET /public/status och GET /public/status/incidents. ServiceHealthSample-modell med TTL (90 dagar), StatusIncident-modell med automatisk incident-skapning vid statusändring. Bakgrundsjobb (CollectStatusSamplesJob) kör var 60:e sekund. Admin-endpoints POST /incidents + PATCH /incidents/{id} för manuell incidenthantering. Uptime beräknas som 30-dagars rullande fönster. www-statussidan hämtar data via client-side fetch med 30s auto-refresh och 5s timeout-fallback. Implemented (PL-T045).

    ✅ PL-T045
  • F16.13.11 Shipped

    Performance profiling (continuous)

    ✅ PL-T288

Disaster Recovery & Restore Drill

F16.14
Planned

**Status:** Implemented (PL-T052)

How it works
  • F16.14.01 Shipped

    DR-runbook med scenariomatris (data-corruption, region-down, credential-leak, ransomware, delete-by-mistake). Per scenario: detection → containment → recovery → post-mortem. Exakta databas-restore-kommandon. RPO 4h / RTO 8h.

    ✅ PL-T052
  • F16.14.02 Shipped

    Automatiserad månatlig restore-drill (GitHub Actions cron 0 4 1-7 * 1). Skapar petanque-drill-<timestamp> via PITR, kör smoke-queries, jämför counts mot prod (±1 %), raderar temp-DB, skickar Slack-rapport.

    ✅ PL-T052
  • F16.14.03 Shipped

    Smoke-test-script (tools/dr-drill-smoke.py) — queries tenants, users, licenses, invoices, tenant_subscriptions. Delta-gräns ±1 %. Exit code 0 = PASS, 1 = FAIL, 2 = prereq-fel.

    ✅ PL-T052
  • F16.14.04 Shipped

    Service principal sp-petanque-dr-drill med custom role Petanque DR Drill Operator (minimala PostgreSQL-rättigheter). Skapas via infrastructure/scripts/create-dr-drill-sp.sh.

    ✅ PL-T052
  • F16.14.05 Shipped

    Key Vault-backup-rutin dokumenterad (soft-delete + purge-protection, az keyvault secret backup/restore).

    ✅ PL-T052
  • F16.14.06 Shipped

    Stripe-nyckelrotationsprocedur dokumenterad (az containerapp secret set + az containerapp update vid credential-läcka).

    ✅ PL-T052
  • F16.14.07 Shipped

    ACS connection-string-rotationsprocedur dokumenterad (az communication regenerate-key + Container App-uppdatering).

    ✅ PL-T052
  • F16.14.08 Shipped

    RPO/RTO baseline i infrastructure/drill-history.md — uppdateras automatiskt efter varje drill-körning.

    ✅ PL-T052

Incident-Response & Alert-Routing

F16.15
Planned

**Status:** Implemented (PL-T053)

How it works
  • F16.15.01 Shipped

    Severity-matris Sev1/Sev2/Sev3 med eskaleringsvägar, responstider och SLA-formulering (docs/engineering/operations/incident-response.md).

    ✅ PL-T053
  • F16.15.02 Shipped

    On-call-rotation dokumenterad (docs/engineering/operations/on-call-rotation.md) med schema, kontaktuppgifter och semesterhantering.

    ✅ PL-T053
  • F16.15.03 Shipped

    Blameless postmortem-mall (docs/engineering/operations/postmortem-template.md) med timeline, root cause, kontribuerande faktorer och åtgärdslista.

    ✅ PL-T053
  • F16.15.04 Shipped

    Azure Monitor action group ag-petanque-primary (e-post + Slack-webhook) provisionerad via infrastructure/monitoring.bicep.

    ✅ PL-T053
  • F16.15.05 Shipped

    Sev1-alerts: 5xx-rate > 1 % / 5 min, database throttling > 10 % / 5 min, ACS e-post bounce > 5 % / 15 min.

    ✅ PL-T053
  • F16.15.06 Shipped

    Sev2-alerts: Container App restarts > 3/h, API-latens p95 > 2 s / 10 min, Stripe webhook-fel > 5 %.

    ✅ PL-T053
  • F16.15.07 Shipped

    Deploy-script infrastructure/deploy-monitoring.sh för att applicera Bicep mot Azure.

    ✅ PL-T053
  • F16.15.08 Shipped

    IncidentLog datamodell med severity (sev1/sev2/sev3), workflow-status (investigating/identified/monitoring/resolved), public_message och updates-lista.

    ✅ PL-T053
  • F16.15.09 Shipped

    GET /public/incidents?since=<iso> — returnerar senaste 30 dagars incidents, publika fält, sortering nyaste-först.

    ✅ PL-T053
  • F16.15.10 Shipped

    POST /admin/incidents + PATCH /admin/incidents/{id} — platform-admin-only, skapar och uppdaterar incidents med severity-klassning.

    ✅ PL-T053
  • F16.15.11 Shipped

    Status-sida (www/src/pages/status.astro + fr/es/sv-varianter) uppdaterad att hämta incident-historik från IncidentLog-collection via /public/incidents. Severity- och workflow-badges i UI.

    ✅ PL-T053

Security Pipeline

F16.16
Planned

**Status:** Implemented (PL-T054)

How it works
  • F16.16.01 Shipped

    Dependabot — veckovisa PR:ar för npm (admin/app/www/packages/shared), pip (api) och månatliga för github-actions. Minor/patch grupperas per ekosystem.

    ✅ PL-T054
  • F16.16.02 Shipped

    Gitleaks secret-scan — kör på varje PR (diff) och push (incremental). Anpassade regler för Stripe-nycklar, Azure connection strings, JWT private keys, Google OAuth-secrets och generiska high-entropy strings (entropi ≥ 4.5).

    ✅ PL-T054
  • F16.16.03 Shipped

    CodeQL statisk analys — Python (api) + JavaScript/TypeScript (admin/app/www/shared). Kör på varje PR och veckovis (söndagar 03:00 UTC). security-extended query-suite.

    ✅ PL-T054
  • F16.16.04 Shipped

    Trivy container-image-scan — integrerat i deploy-api.yml pre-push. Blockar deploy vid CRITICAL/HIGH CVE med fix-version. SECURITY_OVERRIDE-undantag med automatisk Slack-notifiering till #ops-alerts.

    ✅ PL-T054
  • F16.16.05 Shipped

    CVE-policy (docs/engineering/operations/cve-policy.md) — sev-matris, responsible disclosure via security@petanque.life, SECURITY_OVERRIDE-process, retrospektivkrav för CRITICAL.

    ✅ PL-T054
  • F16.16.06 Shipped

    SECURITY.md i repo-rot — publik responsible disclosure-policy med scope, SLA och kontakt.

    ✅ PL-T054

Coverage Gate + Critical-path Tests

F16.17
Planned

**Status:** Implemented (PL-T051)

How it works
  • F16.17.01 Shipped

    pytest-cov coverage-gate — fail_under=80, branch=true, coverage.xml-artefakt uppladdad per PR. Exkluderar migrations, seeds, __init__.py, TYPE_CHECKING-block.

    ✅ PL-T051
  • F16.17.02 Shipped

    PR coverage-kommentar via py-cov-action/python-coverage-comment-action@v3 — informell, continue-on-error=true, blockar inte på kommentarfel.

    ✅ PL-T051
  • F16.17.03 Shipped

    ci-api / coverage som required check — blockar merge vid < 80 % line-täckning.

    ✅ PL-T051
  • F16.17.04 Shipped

    Critical-path testsvit api/tests/critical_path/ — 7 filer, 55+ tester, an in-memory test database, Stripe mockad.

    ✅ PL-T051
  • F16.17.05 Shipped

    ci-api / critical-path som required check — blockar merge vid trasigt billing- eller GDPR-flöde.

    ✅ PL-T051
  • F16.17.06 Shipped

    Frontend coverage soft gate — admin npm test --coverage i CI, continue-on-error=true, artefakt uppladdad 14 dagar.

    ✅ PL-T051

Self-service Data Import

F16.18
Planned

**Status:** Implemented (PL-T056)

How it works
  • F16.18.01 Shipped

    CSV- och Excel-parsing med auto-detektering av delimiter, BOM-stöd och Excel-datumkonvertering.

    ✅ PL-T056
  • F16.18.02 Shipped

    6-stegs import-wizard i admin (/import): välj typ → ladda upp → mappning → dry-run → commit → status.

    ✅ PL-T056
  • F16.18.03 Shipped

    Auto-mappning av kolumnrubriker (case-insensitiv + vanliga aliaser: email, e-post, förnamn, etc.).

    ✅ PL-T056
  • F16.18.04 Shipped

    Dry-run validering: schema-validering + domänvalidering utan att skriva data. Upp till 100 fel inline.

    ✅ PL-T056
  • F16.18.05 Shipped

    Commit i bakgrundsjobb med batch-storlek 500 rader. Progress-uppdatering per batch.

    ✅ PL-T056
  • F16.18.06 Shipped

    Rollback inom 1 timme: tar bort alla created_doc_ids från jobbet. 410 Gone efter fönstret.

    ✅ PL-T056
  • F16.18.07 Shipped

    Konfigurerbar dedup-strategi per jobb: skip, update, fail vid kollisioner.

    ✅ PL-T056
  • F16.18.08 Shipped

    Per-resurs importerare (players, licenses, clubs, memberships) med egna fältscheman och dedup-nycklar.

    ✅ PL-T056
  • F16.18.09 Shipped

    Template-generator: GET /import/templates/{resource} returnerar CSV med rätt kolumner och exempelrad.

    ✅ PL-T056
  • F16.18.10 Shipped

    ImportJob-modell med fullständig audit trail: status, mappning, valideringsfel, skapade doc-ID:n, tidsstämplar.

    ✅ PL-T056

Legal Document Versioning & Signing

F16.19
In progress

**Status:** Kärninfrastruktur (modeller, service, routes, clickwrap) implementerad i PL-T094. DocuSign/BankID-integrationerna returnerar 501 tills provider-credentials landat.

How it works
  • F16.19.01 Shipped

    LegalDocument-modell (platform-scoped) — versionerad per (type, language), SHA-256 content-hash, superseded_by-länk till efterföljande version.

    ✅ PL-T094
  • F16.19.02 Shipped

    LegalDocumentAcceptance-modell (tenant-scoped, append-only) — immutable audit-rad per (tenant, type, version) med IP, UA, content-hash-kopia och signerings-metadata.

    ✅ PL-T094
  • F16.19.03 Shipped

    Automatisk supersede: publish_version sätter superseded_by på tidigare aktiv version för samma (type, language).

    ✅ PL-T094
  • F16.19.04 Shipped

    GET /legal/documents?type=&lang= — publik läsning av aktiv version.

    ✅ PL-T094
  • F16.19.05 Shipped

    POST /legal/documents (platform_admin) — publicera ny version.

    ✅ PL-T094
  • F16.19.06 Shipped

    GET /legal/documents/history (platform_admin) — versionshistorik sorterad på effective_date desc.

    ✅ PL-T094
  • F16.19.07 Shipped

    POST /tenants/{id}/legal/{doc_type}/sign-clickwrap — checkbox-flöde med IP + UA-logg.

    ✅ PL-T094
  • F16.19.08 Shipped

    GET /tenants/{id}/legal/pending?lang= — dokument där tenant saknar acceptance eller aktiv version kräver re-acceptance.

    ✅ PL-T094
  • F16.19.09 Shipped

    GET /tenants/{id}/legal/acceptances — full tenant-acceptanshistorik.

    ✅ PL-T094
  • F16.19.10 Shipped

    is_fully_accepted() — service-helper för onboarding-guard (True när pending-listan är tom).

    ✅ PL-T094
  • F16.19.11 Shipped

    DocuSign eSignature REST v2.1-integration via JWT Grant + RSA-nyckel i Key Vault, create_envelope/get_envelope_status/download_signed_pdf, HMAC-SHA256 webhook-validering.

    ✅ PL-T289
  • F16.19.12 Shipped

    BankID v6 RP-flöde (/sign, /collect, /cancel) via mTLS-cert i Key Vault, default-provider för SE-tenants, PNR-redaktering i loggar och i tjänstelagret.

    ✅ PL-T289
  • F16.19.13 Shipped

    PDF-rendering för kontrakt + legal-dokument med signatur-attest-sida (signer, signed-at, SHA-256, provider-referens). PDF/A-1b-konformans verifieras av verapdf-cli i CI-jobbet ci-api / pdf-a-conformance.

    ✅ PL-T289
  • F16.19.14 Shipped

    Contract.legal_document_acceptances länkar kontrakt till accepterade dokument-versioner. GET /admin/contracts/{id}/legal-coverage exponerar audit-trailen.

    ✅ PL-T289
  • F16.19.15 Shipped

    Onboarding-guard i POST /sys/tenants returnerar 422 onboarding_acceptance_incomplete med missing_documents-lista. Sys-konsolens tenant-create-vy visar checklistan och blockerar submit.

    ✅ PL-T289
  • F16.19.16 Shipped

    Affiliate self-service onboarding (D-AFFILIATE, b1a09359.m-1.t-1). Publika intaget (POST /v1/public/onboarding-requests) accepterar org_type=affiliate + obligatorisk sluten organisation_type (sports_movement/senior_association/parasport/school_sports); tier-katalogen exponerar affiliate-paketet (€999/år). En ren publik affiliate-ansökan auto-godkänns (maskinell flipp av F1-6-beslutet) och auto-provisionerar en live tenant_type=AFFILIATE-tenant (depth 0, hårdkodade restriktioner: ingen licensutfärdning/rankad sanktion/ITC, lösenordslös first-admin) utan förbundsgodkännande. Operatörsskapade affiliates behåller manuellt F1-6-veto. Undertypen bärs framåt till tenant_configs.organisation_type. Se specs/api/endpoints/onboarding-requests.md §D-AFFILIATE + docs/engineering/runbooks/tenant-setup-affiliate.md.

    ✅ b1a09359.m-1.t-1
  • F16.19.17 Shipped

    Affiliate onboarding-UI (D-AFFILIATE, b1a09359.m-1.t-2). Publika onboarding-formuläret (web) utökas med org-typen affiliate: obligatorisk org-undertyps-selektor (4 slutna val, skickas som organisation_type), affiliate-tier-panel (€999/år, ren presentation), ärlig restriktionspanel (kan ej utfärda licens/inga officiella rankningar/ingen hierarki/men kan brygga), skicka-grind (undertyp obligatorisk) och typade klarspråksfel (422 org-typ/undertyp, 409 dubblett). Operatörsinkorgen (sys) utökas med affiliate-filter, org-undertyp, "Auto-provisionerad"-status + auto-provisioneringsresultat, anti-abuse-signaler (endast operatören, aldrig sökanden) och kvarvarande neka/pausa-veto. Endast befintliga openapi-paths, ingen ny rå fetch(. Se specs/web/views/onboarding-request.md §Affiliate + specs/sys/views/onboarding-inbox.md §Affiliate.

    ✅ b1a09359.m-1.t-2

Admin Console Premium UX

F16.20
Planned

**Status:** IA-restrukturering, designsystem, nya admin-vyer, ⌘K-palett, brödsmulor, keyboard shortcuts, rollbaserad menyfiltrering och UX-telemetri implementerade i PL-T099.

How it works
  • F16.20.01 Shipped

    Informationsarkitektur — 14 sektioner konsoliderade till 6 grupper (Översikt, Organisation, Aktivitet, Ekonomi, Kommersiellt, Plattform) i admin/src/navigation/sections.ts. "Coming soon" borttaget.

    ✅ PL-T099
  • F16.20.02 Shipped

    Designsystem — tokens i packages/shared/src/design/native (bottle-green/ivory), admin-bridge admin/src/theme/tokens.ts, ThemeContext med light/dark.

    ✅ PL-T099
  • F16.20.03 Shipped

    Komponentbibliotek — DataTable, FormField + FormSection, Breadcrumbs, CommandPalette, KeyboardShortcutsOverlay, OnboardingBanner som delade primitiver.

    ✅ PL-T099
  • F16.20.04 Shipped

    Dashboard-startsida — hälsning, onboarding-banner, 3 snabbåtgärder, 4 KPI-kort, aktivitetsflöde-placeholder. admin/app/(dashboard)/index.tsx.

    ✅ PL-T099
  • F16.20.05 Shipped

    Fyra top-level admin-vyer — Federations, Licenses, Governance, Discipline. Audit-logg bruten ut till egen route. Se specs/admin/views/.

    ✅ PL-T099
  • F16.20.06 Shipped

    Kommandopalett (⌘K / Ctrl+K) — global sök över nav-items och tenant-switch. Samma synlighetsregler som sidomenyn.

    ✅ PL-T099
  • F16.20.07 Shipped

    Brödsmulor — Breadcrumbs-primitiv mountad i governance, federations, audit, discipline. Sista item inert, tidigare router-länkade.

    ✅ PL-T099
  • F16.20.08 Shipped

    Keyboard shortcuts — ? öppnar cheat-sheet (KeyboardShortcutsOverlay), Esc stänger, Meta+K / Ctrl+K öppnar paletten. Input-typing-suppress.

    ✅ PL-T099
  • F16.20.09 Shipped

    Rollbaserad menyfiltrering — NavItem.requiredTenantRoles: TenantRole[], visibleSectionsFor(tt, isPlatformAdmin, activeRole). RBAC-lins på nav, applicerad på sidomeny + palett.

    ✅ PL-T099
  • F16.20.10 Shipped

    UX-telemetri — lättviktig buffer i admin/src/lib/telemetry.ts med trackRouteView / trackRouteAction, 200-event ringbuffer, opt-out via localStorage.

    ✅ PL-T099
  • F16.20.11 Shipped

    UserDashboardPreferences-endpoint-spec — /me/dashboard-preferences GET/PUT/reset, tenant-scoped, upsert-on-read. Backend-implementation deferrad.

    ✅ PL-T099 (spec)
  • F16.20.12 Shipped

    Widget drag-and-drop med persistens mot /me/dashboard-preferences. Backend-endpoint + frontend-binding via DraggableDashboardGrid (keyboard-first WCAG 2.1.1 AA, HTML5 drag-events på web). 500 ms debounced PUT, optimistisk UI med rollback vid 5xx, "Lägg till widget"-meny + "Nollställ layout"-knapp. Tillämpad på både admin- och sys-konsolens dashboards.

    ✅ PL-T290
  • F16.20.13 Shipped

    Aktivitetsflöde kopplat till audit-stream — senaste 20 entries från GET /audit-log?limit=20 med relativ tid, domän-ikon och EmptyState/Banner-fallbacks. admin/app/(dashboard)/index.tsx.

    ✅ PL-T156
  • F16.20.14 Shipped

    UX-validerings-pipeline för admin-konsolen — testprotokoll i docs/product/admin/usability-test-protocol.md (5 deltagare × 5 kärnuppgifter, SUS-mätning, severity-klassad friktion), avpersonifierad rapport-mall i usability-test-2026-h1.md; Lighthouse-runner tools/lighthouse/run-admin-routes.sh (12 routes × mobile+desktop) + compare-baselines.mjs med absolut floor 90 och regression-tröskel 5 poäng (routes.json); CI-workflow admin-lighthouse.yml (PR + nightly mot prod, PR-kommentar med summary-tabell); admin-baseline visual regression (tools/visual-regression/tests/admin-baseline.spec.ts, 12 routes × {375,1440} × {light,dark} = 48 PNG i Chromium) med dedikerat workflow admin-visual-regression.yml + [accept admin baseline]-marker; budget-doc i docs/engineering/testing/lighthouse-budget.md. Modererat användartest mot 5 icke-tekniska klubbadmins är OWNER-todo (kräver rekrytering + Zoom-sessioner).

    ✅ PL-T291
  • F16.20.15 Shipped

    Dual-mode admin-shell — useDeviceClass() växlar mellan desktop-sidebar och mobile bottom-tab + drawer utan route-byte eller data-remount. 5 bottom-tab-slottar (Översikt / Min org / Aktivitet / Ekonomi / Mer). admin/app/(dashboard)/_layout.tsx, admin/src/components/ui/BottomTabNav.tsx.

    ✅ PL-T156
  • F16.20.16 Shipped

    Design-system-primitives i admin — Text (Fraunces display + Inter UI via Google Fonts på web), SkeletonBlock, Banner, EmptyState i admin/src/components/ui/. Dashboard-vyn är referensimplementation; breddare sweep över 200+ vyer pågår i owner-todo.

    ✅ PL-T156
  • F16.20.17 Shipped

    Tenant-branding injicerad i ThemeProvider — useTenantBranding() hämtar /tenants/{id}/branding vid tenant-byte, ThemeProvider.applyTenantBranding blandar primary-färg med WCAG-aware hover/text/focus-varianter. CSS-var --pl-tenant-primary broadcastas för externa widgets. Dark mode respekterar tenant-primary. admin/src/context/ThemeContext.tsx, admin/src/hooks/useTenantBranding.ts.

    ✅ PL-T156
  • F16.20.18 Shipped

    Guided 5-stegs onboarding-tour för tenant-admins — tenant-config → första licens → första medlem → första tävling → första faktura. Dismiss/later + per-tenant-progress i cross-platform storage. Migrerar till UserDashboardPreferences.onboarding_progress när endpointen landar. admin/src/components/OnboardingTour.tsx.

    ✅ PL-T156
  • F16.20.19 Shipped

    Onboarding-checklist-widget på dashboard-startsidan — renderar wizard-progressionen (6 steg från /onboarding/status), försvinner när wizard + tour är klara, primary-branded CTA som deep-länkar till wizarden.

    ✅ PL-T156
  • F16.20.20 Shipped

    Command palette tillgänglig på mobil — search-ikon i topbaren öppnar samma <CommandPalette> som desktop (⌘K). Modal-layouten skalar till viewport utan separat mobil-komponent.

    ✅ PL-T156

Visual Regression Pipeline

F16.21
Planned

**Status:** Pipeline-infrastruktur, deterministisk seed, CI-workflow och dokumentation implementerade i PL-T113.

How it works
  • F16.21.01 Shipped

    tools/visual-regression/ med Playwright-config, projects-matris (Chromium + Firefox + WebKit × mobile/tablet/desktop = 9), shared helpers för deterministisk runtime-freeze.

    ✅ PL-T113
  • F16.21.02 Shipped

    Spec-filer per app — www.spec.ts, web.spec.ts, admin.spec.ts, app.spec.ts, sys.spec.ts täcker ~25 nyckelvyer. Routes som tillkommer i framtida tasks markeras med test.skip bakom env-flagga.

    ✅ PL-T113
  • F16.21.03 Shipped

    tools/visual-regression/seed.py — drop+återskapa databasen med pinnade ObjectIds, slugs och tidsstämplar. RNG seedad till 42, frozen now 2026-01-15T12:00:00Z.

    ✅ PL-T113
  • F16.21.04 Shipped

    docker-compose.yml i monorepo-roten — PostgreSQL + Redis + petanque-api med healthchecks; återanvänds av integration-tester.

    ✅ PL-T113
  • F16.21.05 Shipped

    Tids-/RNG-/animation-freeze i webbläsaren via freezeRuntime(page) (init-script innan app-kod kör).

    ✅ PL-T113
  • F16.21.06 Shipped

    .github/workflows/visual-regression.yml — PR-triggered, parallell, cachar Playwright-browsers + node_modules. Bryter bygget vid > 0.1 % pixel-diff.

    ✅ PL-T113
  • F16.21.07 Shipped

    [accept visual baseline]-marker i commit-meddelande tillåter workflow att köra --update-snapshots och pusha nya baselines tillbaka till PR-branchen.

    ✅ PL-T113
  • F16.21.08 Shipped

    HTML-rapport (Playwright reporter) laddas upp som GitHub Actions artifact visual-regression-report (14 dagar retention). PR-kommentar med länk vid fail.

    ✅ PL-T113
  • F16.21.09 Shipped

    Tolerance-tröskel maxDiffPixelRatio: 0.001 (0,1 %) global, override per-test för canvas/chart-tunga vyer med dokumenterad motivering.

    ✅ PL-T113
  • F16.21.10 Shipped

    Maintenance-rutiner i docs/product/design-system/visual-regression.md (lägg till vy, uppdatera baseline, tolerance, beslutstabellen "uppdatera vs inte").

    ✅ PL-T113
  • F16.21.11 Shipped

    Kom-igång lokalt i tools/visual-regression/README.md — quick-start, layout-översikt, troubleshooting-tabell.

    ✅ PL-T113
  • F16.21.12 Planned

    Native mobile-screenshot-suite (iOS/Android-simulator) — separat task om web-target visar sig otillräckligt.

    Icebox

DPO Compliance Console & Consent Lifecycle

F16.22
Planned

**Status:** Implemented (PL-T221)

  • F16.22.01 Shipped

    ConsentScopeDefinition model — granular per-purpose consent definitions versioned per (tenant, scope_key) with monotonic version-bump and auto-superseding. Categories: marketing/analytics/operational/subprocessor/photo/research. Existing user consents stay attributable to the exact text the subject saw.

    ✅ PL-T221
  • F16.22.02 Shipped

    DpoAccessRequest model — DSAR-on-behalf with status FSM (draft → submitted → in_review → fulfilled / rejected / cancelled), inline append-only audit_trail mirrored to global AuditLog. Request types: data_export / rectification / erasure / restriction / portability. Mandatory legal_ground.

    ✅ PL-T221
  • F16.22.03 Shipped

    DPO console endpoints /dpo/* — 12 endpoints covering consent-scope CRUD + version-history + retire, access-request lifecycle, DSAR-job proxy, cross-tenant overview. Capability-gated on gdpr:dpo:read / gdpr:dpo:write.

    ✅ PL-T221
  • F16.22.04 Shipped

    New capabilities gdpr:dpo:read, gdpr:dpo:write — registered in role-template catalog, capability-deny logged to AuditLog with action=capability.deny.

    ✅ PL-T221
  • F16.22.05 Shipped

    Admin DPO console — 4 routes (/(dashboard)/dpo overview, /dpo/consent-scopes, /dpo/access-requests kanban, /dpo/cross-tenant) with shared visual primitives. Hidden for affiliate/venue/individual tenant types.

    ✅ PL-T221
  • F16.22.06 Shipped

    Cross-tenant DPO overview — in-memory parallel fan-out per tenant the operator holds gdpr:dpo:read in. Returns per-tenant {open_requests, active_scopes} — no automatic cross-tenant DB join. Operator's own tenant always allowed; system-scope tokens get full body.tenants.

    ✅ PL-T221
  • F16.22.07 Shipped

    Reusable AuditTrail admin component — shared timeline rendering used by DPO request detail and earmarked for future governance/anti-doping/finance consoles.

    ✅ PL-T221
  • F16.22.08 Shipped

    Audit-log integration — DPO-action category=gdpr_dpo, scope-version-action category=gdpr_consent_versioning. Hash-chain (F16.09) garanterar tamper-evidence on the global trail.

    ✅ PL-T221

Systemmeddelanden (sys → admin)

F16.23
Planned

**Status:** Implemented — rebuild, initiativ **D-ANNOUNCE** (ägarbeslut 2026-07-01), milstolpe `bf88c558.m-1` (t-1 backend, t-2 frontend + harness).

  • F16.23.01 Shipped

    Datamodell announcements + announcement_receipts (migration 00131) — typ/ikon/innehåll/översättningar, giltighetsfönster (UTC-filtrering, display_timezone endast visning), placering (login/post-login, position, prioritet), målgrupp (alla/valda förbund + rollnycklar), beteende/a11y (dismissible, remember_dismissal, aria_role, contrast_mode) och livscykel. Det effektiva tillståndet (draft/scheduled/active/expired/unpublished) HÄRLEDS i SQL.

    ✅ bf88c558.m-1.t-1
  • F16.23.02 Shipped

    Sys-CRUD /v1/sys/announcements (lista/skapa/detalj/redigera/publicera/avpublicera/duplicera) bakom nya capabilityn sys.announcements.manage (sys_support + sys_security). Audit i SAMMA transaktion; detaljen bär audit-historik, räckvidd och kvittenstal.

    ✅ bf88c558.m-1.t-1
  • F16.23.03 Shipped

    Publik least-priv-läsning GET /v1/public/announcements/login (@public, Cache-Control: max-age=60) — endast publicerade meddelanden i fönstret med show_on_login och målgruppen "alla förbund"; målgrupp/skapare/statistik läcker aldrig.

    ✅ bf88c558.m-1.t-1
  • F16.23.04 Shipped

    Autentiserad läsning GET /v1/announcements/active — tenant/roll-matchning ENBART ur bearern, bokför seen_at, exkluderar server-kvitterade avfärdanden. Kvittenser via POST /v1/announcements/{id}/receipt (dismiss/acknowledge, idempotent; 409 för icke-stängbart, 404 för meddelanden anroparen inte matchar).

    ✅ bf88c558.m-1.t-1
  • F16.23.05 Shipped

    Estimerad räckvidd GET /v1/sys/announcements/reach — {tenant_count, admin_count} räknat ur riktiga tenants/role_assignments, aldrig konstanter.

    ✅ bf88c558.m-1.t-1
  • F16.23.06 Shipped

    Sys-kompositören /systemmeddelanden — lista med serverfiltrerade chips + typfilter och riktiga states, kompositör med skärm-kontraktets alla sex steg (innehåll, giltighetsfönster, placering, målgrupp, beteende & a11y, granska & publicera) och detaljvy med livscykel, räckvidd/kvittenser och audit-tabell. Nav-post och yta capability-gatade fail-closed.

    ✅ bf88c558.m-1.t-2
  • F16.23.07 Shipped

    Admin login-banner ovanför inloggningskortet — aldrig stängbar, role per meddelandets aria_role, tyst degradering (inloggningen blockeras aldrig) och det frysta login-testID-kontraktet orört.

    ✅ bf88c558.m-1.t-2
  • F16.23.08 Shipped

    Admin app-banner i skalet + critical-overlay — position top/dashboard/båda, stapling på prioritet + starts_at, dismiss med server-kvittens (överlever omladdning) resp. sessionslokal döljning, role="alertdialog" med fokusfälla och bekräftelsekvittens, uppdatering var 120:e sekund + vid fokus/navigering. Bekräftelsen är SERVERNS (acknowledged i /v1/announcements/active), så en bekräftad overlay aldrig återkommer i en ny flik/session.

    ✅ bf88c558.m-1.t-2
  • F16.23.11 Shipped

    Rollkatalog GET /v1/sys/announcements/roles (samma capability) — förbundsrollernas nyckel + namn + antal bärare ur roles/role_assignments; kompositörens rollfilter är en flervalsväljare med rollernas NAMN, och listan/detaljen visar namnen i stället för råa nycklar.

    ✅ bf88c558.m-1.t-2
  • F16.23.09 Shipped

    Delad bannerkälla @petanque-life/shared/announcements-ui — sys-förhandsvisningen och admins banners renderas av SAMMA komponent (färg aldrig ensam bärare, WCAG 2.2 AA, högkontrastläge).

    ✅ bf88c558.m-1.t-2
  • F16.23.10 Shipped

    Verifieringssele: integrationssviten TestDAnnounceM1 (riktig Postgres) + L4-drivrutinen tools/gate/e2e.d/470-dannounce-m1.mjs som kör hela flödet sys→admin i en riktig webbläsare mot den körande stacken, inkl. axe utan critical/serious.

    ✅ bf88c558.m-1.t-2

Tenant-lös referenskatalog (nationer, discipliner, tävlingsformer)

F16.24
In progress

**Status:** Implemented — rebuild, initiativ **C-1 — Referensdata-seed**, milstolpe `2c5f5552.m-1` (t-1 schema + idempotent seed-runner, t-2 publika läsningar + OpenAPI/TS-klient). Den tenant-skopade halvan — tenant-defaults, Sverige-demo, `sandbox-sys` och icke-riktig-markeringen — levererades av `2c5f5552.m-2` och inventeras i **F16.25** nedan (tenant-onboarding-defaults i `01-federation-governance.md` F01.11). Seed-konsolen — API-halvan (`/v1/sys/seed/*` + `/v1/sys/reference/*`, t-1) **och** operatörsytan `/ops/referensdata` i `sys`-appen (t-2) — levererades av `2c5f5552.m-3` och inventeras i **F16.26** nedan.

  • F16.24.01 Shipped

    Datamodell nation / discipline / game_format / ranking_config_template / seed_run (migration 00133) — inget tenant_id: katalogerna delas av alla förbund. iso3 och is_fipjp_member är nullbara (obelagt är NULL, aldrig en gissad default) och code_standard skiljer ISO 3166-1 från ISO 3166-2-subdivisioner och den användartilldelade sandbox-koden.

    ✅ 2c5f5552.m-1.t-1
  • F16.24.02 Shipped

    Idempotent seed-runner api seed-reference-data (CLI) / run-job seed_reference_data (jobb) — samma referencedata.Reconcile, allt i EN transaktion, upsert på naturlig nyckel där en oförändrad rad inte får någon ny radversion. Varje körning bokförs i seed_run med innehållshashad seed_version; manuell drift läks, elo_config skrivs aldrig över.

    ✅ 2c5f5552.m-1.t-1
  • F16.24.03 Shipped

    Seedinnehållet: 43 nationer med belagd källa per rad (source_ref), 5 discipliner, 5 tävlingsformer, 5 ranking-mallar materialiserade ur internal/ranking (inga omskrivna poängtabeller). FIPJP-rostern är medvetet ofullständig och fipjp-world/cep-continental seedas som verification_pending-stubbar — inga uppfunna siffror.

    ✅ 2c5f5552.m-1.t-1
  • F16.24.04 Shipped

    Fyra publika, anonyma läsningar GET /v1/public/reference/{nations,nations/{iso2},disciplines,game-formats} (@public, varken bearer eller X-Tenant-Id) med Cache-Control: public, max-age=3600, envelope {"data":[…]}, stabil sortering och tri-state-filtret ?fipjp_member=. Inaktiva rader (sandbox-nationen) är osynliga publikt; katalogen är read-only (skrivförsök → 405).

    ✅ 2c5f5552.m-1.t-2
  • F16.24.05 Shipped

    Felkontrakt + kontrakt: 404 nation_not_found, 404 discipline_not_found (nya koder i problem.Registry och problem.schema.json x-pl-codes), 422 validation_failed på ogiltiga filtervärden — alla RFC 9457. De fyra rutterna deklarerade i openapi.json (tagg cross-tenant-public-read) med schemana ReferenceNation/ReferenceDiscipline/ReferenceGameFormat, och den genererade TS-klienten i synk.

    ✅ 2c5f5552.m-1.t-2

Demo-tenant, `sandbox-sys` och icke-riktig-markeringen

F16.25
Planned

**Status:** Implemented — rebuild, initiativ **C-1 — Referensdata-seed**, milstolpe `2c5f5552.m-2`

  • F16.25.01 Shipped

    seed-demo-tenant (CLI) / run-job seed_demo_tenant (jobb) — samma reconcile bakom båda vägarna, allt i EN transaktion. Sverige-demon utvidgar den befintliga SWE-tenanten additivt: 8 distrikt med sverige.md:s namn (befintliga nodkoder och id:n bevaras), 6 klubbar varav de 5 namngivna demo-klubbarna, spelklasser M/YJ/J/Ö/D/V55/V65, tävlingsnivåer, licenstyper med avgifter som heltal i minsta enhet + ISO 4217, samt licenser/spelare/en sanktionerad tävling med publicerat resultat och en publik ranking-preset. En redan ifylld katalog skrivs aldrig över.

    ✅ 2c5f5552.m-2.t-2
  • F16.25.02 Shipped

    seed-sandbox (CLI) / run-job seed_sandbox (jobb) — provisionerar sandbox-sys (tenant_type='sandbox', country_code='XS', default_locale='en', depth 0) med tenant_configs ur samma defaults-väg som varje annan tenant, ett tenant_theme och den deterministiska aktören sandbox-admin.test@petanque.life. Seed-vägen är den enda som får skapa den reserverade koden.

    ✅ 2c5f5552.m-2.t-2
  • F16.25.03 Shipped

    Idempotens som mätning, inte påstående — båda seedarna upsertar på naturlig nyckel, hashar sitt innehåll till seed_version och bokför varje körning i seed_run (även no-op: status='already_current', rows_upserted=0). En andra körning lämnar radantal och radversioner (xmin) orörda.

    ✅ 2c5f5552.m-2.t-2
  • F16.25.04 Shipped

    Icke-riktig-markeringen — EN delad, icke-avfärdbar banner i packages/shared/src/sandbox-ui/ ("SANDBOX — not real data" via i18n-nyckeln sandbox.banner, role="status", warning-tokens ur design/tokens/colors.mjs). Den monteras i admins skal (ShellHost) och i appens rot-layout genom en runtime-renderare per yta (DOM/RN) ovanpå EN gemensam kärna — beslut, signal, palett och copy finns bara på ett ställe. Kontrast ≥ 4.5:1 i ljust, mörkt och båda högkontrastteman (mätt mot tokenkällan i sandbox-ui/__tests__).

    ✅ 2c5f5552.m-2.t-3
  • F16.25.05 Shipped

    Signalen är den AKTIVA tenantens tenant_type — läst ur GET /v1/me/tenants genom ytans enda ApiClient och den genererade klienten (noll råa fetch(, ratchet-baselinen oförändrad på 0). Aldrig en miljöflagga, aldrig ett kodprefix: en tenant som *heter* sandbox men är federation får ingen banner, och en sandbox kan inte gömmas (ingen stäng-affordans, ingen dismissed-flagga — den överlever en omladdning).

    ✅ 2c5f5552.m-2.t-3
  • F16.25.06 Shipped

    Verifieringssele — de två seedarna körs i grindens seed-steg (tools/gate/verify.sh + L4-seedmängden), och L4-drivrutinen tools/gate/e2e.d/590-c1m2-sandbox-banner.mjs kör acceptanssviten tests/e2e/admin/sandbox-banner.spec.ts i både admin-desktop och admin-mobil mot den körande stacken: bannern syns för sandbox-aktören (med axe-ren kontrast/roll), saknas för SWE-aktören.

    ✅ 2c5f5552.m-2.t-3

Sys-seedkonsolen: köra, följa och granska C-1:s seedar

F16.26
Planned

**Status:** Implemented — rebuild, initiativ **C-1 — Referensdata-seed**, milstolpe

  • F16.26.01 Shipped

    Seed-sömmen — de tre kinderna seed_reference_data/seed_demo_tenant/seed_sandbox registreras på HTTP-edgens och workerns ytor (tidigare bara den schemalagda), så en 202-enqueue både kan köas och dispatchas. Kind-namn, scheman och cron-strängar är oförändrade: den frysta api/testdata/job_registry.golden är byte-identisk.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.02 Shipped

    Körkontexten reser i payloaden, inte i miljön — {run_token, ran_by} valideras av kindens Validate vid enqueue ({} fortsatt giltig; okänt fält, ogiltig uuid eller ran_by > 120 tecken ⇒ 422 vid edgen). En worker kör många jobb i samma process, så os.Setenv per jobb vore ett race som attribuerar fel operatör; ett mekaniskt test håller seed-/jobbvägen fri från det. PL_SEED_RAN_BY kvarstår som fallback för CLI/deploy.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.03 Shipped

    Bokföringen blir observerbar (migration 00134, strikt additiv) — seed_run.status (seeded\

    already_current, backfillad med runnerns egen regel) och seed_run.run_token (uuid, unikt där det inte är NULL) skrivs på båda körvägarna. CLI:ts enda JSON-rad på stdout är oförändrad (fem fält). | ✅ 2c5f5552.m-3.t-1
  • F16.26.04 Shipped

    Köra en seed — POST /v1/sys/seed/{reference-data,demo-tenant,sandbox} ⇒ 202 {job_id, run_token, seed_name, kind, status}. reason är obligatoriskt (hamnar på jobbraden och i auditraden); samma idempotency_key igen ⇒ 200 med samma job_id/run_token, en jobbrad och en auditrad — dubbelklicket blir aldrig två körningar. Aldrig 409: already_current är en körningsstatus, inte ett HTTP-fel, så manuell drift alltid kan läkas.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.05 Shipped

    run_token binder klick → jobb → körning — HTTP-lagret genererar uuid:t, jobbets payload bär det, seeden skriver det i seed_run, och historiken härleder job_id via jobs.payload->>'run_token'. seed_run_id finns medvetet inte i 202-svaret: raden existerar inte än, och att skicka null vore ett påstående.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.06 Shipped

    Status — GET /v1/sys/seed/status ger exakt tre poster (en per seed) med latest_run / in_flight / last_unsuccessful. En seed som aldrig körts utelämnas inte: latest_run: null är beskedet. in_flight gör att konsolen kan säga "köad" utan att ljuga (t.ex. när ingen worker kör), och last_unsuccessful gör att den aldrig fastnar i "kör…" för en körning som avbröts eller misslyckades och därför aldrig blir en seed_run-rad.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.07 Shipped

    Historik — GET /v1/sys/seed/runs, husets {data, page}-envelope, ran_at desc, id desc, keyset-paginerad, filter ?seed=/?run_token=. En konsolkörning bär run_token + härlett job_id; en CLI-/deploy-körning bär null i båda — det är sanningen, inte en lucka. Querysträngen är deny-by-default: okänd parameter, okänt värde eller ogiltig cursor ⇒ 422, aldrig en tyst tom lista.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.08 Shipped

    Operatörens katalogläsningar — GET /v1/sys/reference/{nations,disciplines,game-formats,ranking-templates} med Cache-Control: no-store och inaktiva rader synliga (sandbox-nationen XS), till skillnad från m-1:s publika, cachade läsningar som är oförändrade. source_status är obligatoriskt i varje ranking-mall, så en verification_pending-stubb aldrig är oskiljbar från en bekräftad mall. Read-only: skrivförsök ⇒ 405.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.09 Shipped

    En diskriminerande grind, fail-closed — läsning kräver sys.console.access, körning sys.engineering.tools + färsk auth. Nekande ger 403 platform_admin_required (ny kod i problem.Registry och problem.schema.json x-pl-codes), aldrig 404: rutten finns och nekar. Anonymt ⇒ 401, inaktuell inloggning ⇒ 401 reauth_required. Alla nio rutter är självgatade och listade i self_guard_baseline.txt; husets grindhjälpare är oförändrade. Ett nekande lämnar noll skrivningar och noll sys.seed.run-auditrader.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.10 Shipped

    Kontrakt och vakter — alla nio rutter deklarerade i openapi.json (tagg sys) med fullständigt felkontrakt, den genererade TS-klienten i synk (make api-ts-client-check), make spec-check och api authz-check gröna. Testfixturen sys-engineer.test@petanque.life (sys_user_roles: sys_engineer) seedas av seed-authz — en testidentitet i grindens dev-login-allowlista, ingen produktionsidentitet och ingen försvagad kontroll.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.11 Shipped

    Verifieringssele — integrationssviten TestC1M3* kör hela vägen mot riktig Postgres och den byggda binären: POST ⇒ 202 ⇒ jobbrad ⇒ api worker dispatchar ⇒ seed_run med rätt run_token/ran_by/status, re-run mätt på radversion (xmin) och inte på radantal, två operatörer i samma workerprocess med två sanna attributioner, samt ett riktigt POST /v1/sys/jobs/{id}/cancel för last_unsuccessful.

    ✅ 2c5f5552.m-3.t-1
  • F16.26.12 Shipped

    Sys-vyn — /ops/referensdata i den byggda sys-appen (navpost sys-nav-referensdata direkt efter /ops/jobs, samma sys.console.access-grind: ingen session ⇒ /login, ingen konsolåtkomst ⇒ neutral "Inte tillgänglig" och dold navpost). Tre körkort med obligatoriskt skäl vars kvittens är ärligt asynkron: Startar → Köad → Kör → utfall, där varje övergång är läst ur GET /v1/sys/seed/status/runs (run_token binder klicket till dess seed_run-rad) — aldrig ur klientens antagande. Utfallet visar innehållsversion, antal upsertar och seeded/already_current; ett failed/cancelled/dead_letter-jobb redovisas som just det med jobb-id och länk till /ops/jobs. Pollningen går i jobbkonsolens kadens (2 s) och stoppas när inget jobb är i flykt.

    ✅ 2c5f5552.m-3.t-2
  • F16.26.13 Shipped

    Historik och kataloger blir läsbara — seed_run-tabellen (seed · version · tid · upsertar · utfall · körd av · jobb) med filter per seed och keyset-"visa fler"; en CLI-/deploy-körning visar ärligt — (CLI/deploy) i stället för ett påhittat jobb-id, och tom historik ger ett ärligt tomt tillstånd, aldrig en fabricerad rad. Katalogvyerna är read-only mot operatörsrutterna (no-store, inaktiva rader synliga): nationer med fritextsök + konfederations- och FIPJP-filter skickade till servern (radantalet är API:ts svar, aldrig en klientfiltrerad cache), discipliner, tävlingsformer och ranking-mallar där source_status står i klartext ("Väntar på källa"). Demo-/sandbox-statuskorten byggs på GET /v1/sys/tenants och drivs av tenant_type — aldrig av namn eller en frontend-flagga.

    ✅ 2c5f5552.m-3.t-2
  • F16.26.14 Shipped

    Transport, tillstånd och verifieringssele — all datatrafik genom ytans enda ApiClient (useApiClient() + sysOps.*/tenantsOps.*): noll råa fetch(, ratchet-baselinen oförändrad 0, och inget anrop till en rutt utanför openapi.json. Riktiga laddnings-, tom-, fel- och behörighetsnekat-tillstånd (körknappen avaktiverad utan sys.engineering.tools, med förklaring — API:t nekar ändå med platform_admin_required), aria-live på körstatusen, ≥48 px träffytor. Bevisen: en nätverkslös strukturgrind (sys/__acceptance__/c1m3-referensdata-seedkonsol.acceptance.mjs) plus L4-drivrutinen tools/gate/e2e.d/600-c1m3-seedkonsol.mjs som mot körande stack kör en riktig seed, jämför kvittensen kolumn-för-kolumn med GET /v1/sys/seed/runs, bevisar diskriminering genom en riktig databasmutation (återställd i finally) och kör axe utan critical/serious i både desktop och 390 px.

    ✅ 2c5f5552.m-3.t-2