Federation Onboarding & Migration
At a glance
Federation Onboarding & Migration is the white-glove pipeline that takes a new federation from blank tenant to live operations — a stateful onboarding wizard, CSV/XML bulk import with validate/dry-run/apply modes, source-system migration plans, sandbox pilots, idempotent data cleansing, and per-batch rollback. It also covers standalone clubs migrating into a federation tenant.
How it works
The onboarding wizard at /federation-onboarding/wizard is a state machine: tenant-config → districts → clubs → players → review → complete. Drafts are accumulated as the operator advances, and only on complete does the platform materialise them into real TenantConfig, District, Club and PlayerProfile documents in a transactional sequence. Bulk import is stateless under /federation-onboarding/imports/* and supports three modes: validate (rows are parsed and rule-checked, nothing is written), dry_run (a full diff report classifies every row as new, update, unchanged or error) and apply (writes happen).
Validation respects TenantConfig.org_hierarchy_depth, so the same rule set evaluates a flat Norwegian import and a deep French one. Before validation runs, an idempotent cleansing pipeline normalises every field — names trimmed, code/district_code uppercased, status lowercased, email lowercased, phone numbers reduced to plus-prefix-and-digits, dates rewritten from DD/MM/YYYY, DD.MM.YYYY, DD-MM-YYYY, YYYY/MM/DD or YYYY.MM.DD into ISO YYYY-MM-DD when unambiguous (ambiguous MDY is left for the validator to reject). Every rewrite is reported as a warning on ImportRowReport with cleaned_count on ImportReport so the operator sees exactly what was changed.
The migration-plan generator is a side-effect-free function that returns an ordered MigrationPlan: prepare_source → create_sandbox → import_districts → validate_clubs → dry_run_clubs → apply_clubs → validate_players → apply_players → pilot_signoff → production_cutover, with source-specific warnings (FFBE/SBF/DPV quirks) and risk levels that auto-escalate on volume (>5000 players → high; >500 clubs on low baseline → medium). For flat hierarchies or requires_sandbox_pilot=false the plan strips the district and sandbox steps. Sandbox tenants wrap a cloned TenantConfig as a SandboxTenant with lifecycle active → promoted or active → discarded, idempotency-protected with 409 on invalid transitions; discard soft-deletes the underlying config for audit.
Rollback is captured by ImportBatch — every apply run records created_ids and pre-mutation update_snapshots; manual rollback uses /imports/batches/{id}/rollback, and a mid-apply failure triggers automatic rollback that stamps the batch auto_rolled_back. Both paths log structured audit events. The standalone-club path (F01.09, PL-T010) is a four-step admin wizard that moves Club, License, Match, Membership, Ranking and CalendarEvent documents from the standalone tenant into the target federation, sets the source read-only, and offers a 30-day rollback window with conflict-resolution policies (skip/rename/overwrite) on license-number and club-code collisions.
Key capabilities
- Walk a new federation through a stateful wizard from tenant config to first players.
- Import clubs and players in CSV/XML with validate, dry_run and apply modes.
- Cleanse imported data idempotently before validation and report every rewrite as a warning.
- Generate a source-system-aware migration plan with risk escalation and ordered steps.
- Spin up sandbox tenants for pilot installation with promote/discard lifecycle.
- Roll back any apply run manually or automatically on mid-apply failure with full audit.
- Migrate standalone clubs into a federation tenant with consent, conflict resolution and 30-day rollback.
In practice
On a Tuesday morning the IT lead of a new national federation runs a full onboarding rehearsal. He generates a migration plan, the planner tags his volume (8 200 players) high-risk and recommends a sandbox pilot. He provisions a sandbox, runs the club CSV in validate mode (32 cleansing warnings, 4 errors), fixes the 4 rows, runs dry_run (212 new, 18 update, 0 unchanged, 0 error), then apply — an ImportBatch is created with all 230 created_ids.
Two days later a player CSV apply hits a database write error half-way; auto-rollback kicks in, the batch is stamped auto_rolled_back, the structured audit event federation_onboarding.import_auto_rollback fires. After fixing the source data he reruns the import successfully, signs off the pilot, and flips the sandbox to promoted for production cutover.
Features in this subsystem
24| ID | Status | Features |
|---|---|---|
| F01.08.01 | Shipped | Self-service onboarding wizard for new federations ✅ PL-F0108a |
| F01.08.02 | Shipped | Bulk import of clubs and players (CSV, XML) ✅ PL-F0108a |
| F01.08.03 | Shipped | Business-rule validation of imported rows per tenant ✅ PL-F0108a |
| F01.08.04 | Shipped | Dry-run diff report (new/update/unchanged/error) ✅ PL-F0108a |
| F01.08.05 | Shipped | Migrationsplan-generator (källsystem → Petanque Life) — POST /federation-onboarding/migration-plans returnerar ordnade steg, källsystemsspecifika varningar och risknivåer (FFBE/SBF/DPV-heuristik, volymbaserad eskalering) ✅ PL-F0108b |
| F01.08.06 | Shipped | Sandbox-tenant för pilotinstallation — /federation-onboarding/sandboxes wrappar TenantConfig, lifecycle active → promoted \ discarded, discard soft-deletar konfigen | ✅ PL-F0108b |
| F01.08.07 | Shipped | Datavalidering och rensning under import — idempotent städpipeline före validering, per-fält-normalisering (whitespace, casing, email, telefon, DD/MM/YYYY → ISO), omskrivningar rapporteras som warnings/cleaned_count ✅ PL-F0108b |
| F01.08.08 | Shipped | Rollback av import vid fel — ImportBatch spårar created_ids + pre-mutation update_snapshots, manuell rollback via /imports/batches/{id}/rollback och auto-rollback vid mid-apply-fel (stämplas auto_rolled_back) ✅ PL-F0108b |
| F01.09.01 | Shipped | Dry-run migration plan with per-collection document counting and conflict detection ✅ PL-T010 |
| F01.09.02 | Shipped | Consent flow — one-time token invitation to club admin, acceptance endpoint ✅ PL-T010 |
| F01.09.03 | Shipped | Migration execution — move documents, update tenant_id, set source read-only ✅ PL-T010 |
| F01.09.04 | Shipped | 30-day rollback window — revert all migrated documents, lift read-only ✅ PL-T010 |
| F01.09.05 | Shipped | Conflict resolution — license number / club code collision (skip/rename/overwrite) ✅ PL-T010 |
| F01.09.06 | Shipped | Four-step admin wizard UI with migration dashboard and rollback controls ✅ PL-T010 |
| F01.10.01 | Shipped | Adaptiv, org-medveten guide — ramfrågor (structure/licensed/preset) formar stegen; GET /v1/onboarding/guide returnerar variant + framing + härlett stegset + readiness[] + recommended_next[]; POST /v1/onboarding/framing sparar svaren och räknar om stegseten idempotent (If-Match) ✅ f154e12a.m-1.t-1 (BACKEND) |
| F01.10.02 | Shipped | Per-variant stegset (federation/distrikt/klubb/venue) beräknat server-side ur org-typ + framing; klient-satt ?variant/tenant_id ignoreras ✅ f154e12a.m-1.t-1 |
| F01.10.03 | Shipped | Klubbens avgift ovanpå federationslicensen — club_fee-steget skriver via befintliga D-ORGS PUT /v1/tenant/license-types/{id}/fees (license_fee_component + org_node_id + pricing-guard) ✅ f154e12a.m-1.t-1 |
| F01.10.04 | Shipped | Utfalls-mätare (readiness[]) data-driven ur riktig konfiguration (licenstyper, sanktionerande nivåer) — aldrig hårdkodade booleans ✅ f154e12a.m-1.t-1 |
| F01.10.05 | Shipped | Aktörsattribuering + resumerbarhet — step_status jsonb spårar per-steg state + actor (tenant/sys); steg-komplettering registrerar aktören ✅ f154e12a.m-1.t-1 |
| F01.10.06 | Shipped | Hoppa-över + hem-checklista — POST /v1/onboarding/steps/{step}:skip (icke-obligatoriska steg → skipped); obligatoriskt steg → 422 required_step_not_skippable; steg utanför set → 422 step_not_in_set ✅ f154e12a.m-1.t-1 |
| F01.10.07 | Planned | Betal-steg (Connect payments — ✅ m-3), no-touch medlemsimport (✅ m-4, se F03), admin-vyer 🔜 syskon-milstolpar |
| F01.10.08 | Shipped | Sys warm-start vid approve (D-SIGNUP-2 m-5, backend) — F1-6:s approve-body utökad ADDITIVT med valfritt warm_start (preset/structure/tier/queue_member_import); validerat mot guidens BEFINTLIGA closed sets FÖRE beslutstransaktionen (ogiltigt → 422 med warm_start.*-fältfel, atomärt — ingen tenant); seedning i SAMMA transaktion som provisioneringen (framing + server-härledd variant + köad member_import som MARKERING + requested_tier-kod framåtburen — inga pengar); auditerat onboarding.warmstart.seed i TENANTENS logg attribuerat den beslutande operatören (aldrig "system"); idempotens/409-no-op ärvd; aldrig låst (framing omskrivbar). Guiden får additivt toppnivå-warm_start (actor_id, queued_steps) + per steg queued. Spec: specs/api/endpoints/onboarding-warmstart-assist.md — f154e12a.m-5.t-1 ✅ f154e12a.m-5.t-1 |
| F01.10.09 | Shipped | Assist — consent-gated sam-fyllning (D-SIGNUP-2 m-5, backend) — steg-skopat, återkallbart, tidsbegränsat (TTL 7 d) engångs-samtycke onboarding_assist_consent (00095; max ETT öppet per {tenant, steg} via partiellt unikt index): tenant POST /v1/onboarding/steps/{step}:request-assist :revoke-assist (samma gate + stegset-grind som :complete; idempotent; auditerat); sys GET /v1/sys/onboarding-assists (inkorg), GET /v1/sys/tenants/{id}/onboarding/guide (läs endast m. levande samtycke) och POST /v1/sys/tenants/{id}/onboarding/steps/{step}:complete — genom SAMMA stepDelegate-validering/If-Match som tenant-vägen; step_status={done, sys, <op>} ("Set up for you"-producenten), audit i tenantens logg attribuerad operatören, samtycket förbrukas (422 förbrukar inte). Deny-by-default: allt utan levande steg-exakt samtycke (inkl. okänd tenant) → identiskt opakt 403 — ingen enumeration, ingen D-SYS-impersonation. Domän: docs/domain/onboarding-assist-consent.md — f154e12a.m-5.t-1 | ✅ f154e12a.m-5.t-1 |
| F01.10.10 | Shipped | Sys warm-start-panel + assist-yta, admin assist + warm-start-banner (D-SIGNUP-2 m-5, frontend) — sys ärendedetaljen får "🤝 Warm start (valfritt)"-panelen (ENBART beslutbara ärenden — submitted/in_review; preset/strukturdjup/tier — tier-katalogen DATA-driven ur GET /v1/public/onboarding/tiers — + köa-import-checkbox; valen rider som warm_start på den BEFINTLIGA approve-mutationen; 422 warm_start.*-fältfel renderas per fält) och "Assistera en pågående tenant"-ytan (GET /v1/sys/onboarding-assists → assist-läge: tenantens guide-läsmodell via GET /v1/sys/tenants/{id}/onboarding/guide + sam-fyll DET samtyckta steget via POST …/steps/{k}:complete; opakt 403 → ärligt felstate, aldrig krasch). Admin-guiden får warm-start-bannern (guide.warm_start), "🤝 Set up for you" på warm-startat innehåll (prefilled/köat + completed_by_actor='sys'), köad-import-markeringen (step.queued) och steg-generisk "🤝 Be Petanque Life sätta upp det här →"-begäran med status + återkallning (assist_state, :request-assist/:revoke-assist). Allt via den genererade klienten mot riktiga endpoints — inga hårdkodade badges. Vy-spec: specs/sys/views/onboarding-inbox.md + specs/views/admin-onboarding-guide-views.md — f154e12a.m-5.t-2 ✅ f154e12a.m-5.t-2 |
Related subsystems
Stakeholders who need this subsystem
Surfaces in 1 stakeholder analyses