9f3c545c46
Core performs a GAME-AGNOSTIC transactional profile import; all FIFA17 semantics (manifest parse, wire ids, resourceId/nextItemId, squad extension v1, CardDefinitionId/OwnedItemId choice) stay in openfut-import-fifa17. Core sees only opaque ids and opaque extension bytes. services::import::apply_profile_import(pool, card_db, ProfileImportRequest): - ONE SQLite transaction installs profile + club + all owned cards + canonical squad + one opaque game extension; commits together or not at all. - Definition preflight (pre-tx): every owned card_id MUST resolve in loaded production content, so ownership never points at absent content. - Squad all-or-nothing (pre-tx): every active-squad OwnedItemId MUST be in the imported ownership set. - OwnedItemId uniqueness + generic extension bounds enforced pre-tx. - Core computes the canonical squad fingerprint itself (never adapter-supplied) and persists the extension atomically, exactly as the live squad-write path. Rerun identity via profiles.import_fingerprint (migration 0018, nullable): - identical source_fingerprint on an already-imported game -> idempotent no-op; - differing token -> fail (needs explicit update mode); - pre-existing non-imported profile -> never clobbered. So a crash after identity-seeding re-runs cleanly with no cleanup/reminting. CLI: 'openfut-core import <request.json>' loads production content packs, parses a generic request, applies. squad_fingerprint made pub(crate) for reuse. 8 import-service tests (happy path, idempotent rerun, fingerprint mismatch, missing-definition no-write, squad-not-owned, dup OwnedItemId, non-imported clobber guard, empty-owned). clippy -D warnings clean; full suite 159 green.