fix(fifa17): map market resource ids to authoritative Core card ids

Closes the market correctness gap: handle_market_list recorded listing.card_id
from the raw FIFA wire resourceId, so a synthetic buy minted a card_id Core
could not resolve — it survived the immediate response but Core's content
preflight rejected it on reboot.

- catalog.rs: keep the by_resource reverse index (was built then discarded) and
  expose `card_id_for_resource(resource_id) -> Option<&str>` — exact reverse of
  the card_id->asset catalog, no heuristics, unknown => None.
- lib.rs: `impl MarketCardResolver for Fifa17IdentityResolver` delegates to the
  same catalog /club shaping uses; Core never sees a FIFA resource id.
- market_store.rs: listings now carry BOTH `card_id` (authoritative Core content,
  what a buy MINTS) and `wire_resource_id` (the FIFA wire id, echoed in the
  auction record). New column; create_listing takes both; row/Listing updated.
- market.rs: `MarketCardResolver` trait; handle_market_list resolves resourceId
  -> Core card_id and fails closed (persists nothing) on an unmappable resource;
  auction_record emits `resourceId` from wire_resource_id. Dispatch passes the
  resolver.

Tests: list_unknown_resource_fails_closed_no_listing (B),
list_persists_core_card_and_wire_resource_across_reopen (C), catalog reverse
lookup; and the dispatch E2E now RESTORES the full Core+store restart
(economy_full_sequence_through_dispatch_and_restart) — the synthetic buy mints a
real reverse-mapped card_id, so Core's content preflight passes on reboot (A+D).
market 23 lib + catalog 15 + 2 integration green; clippy -D warnings + fmt clean.
This commit is contained in:
OpenFUT Agent
2026-08-13 21:43:48 +00:00
parent 884ecbba64
commit fe72f0def2
5 changed files with 217 additions and 51 deletions
+15 -1
View File
@@ -125,6 +125,10 @@ fn default_rareflag() -> i64 {
#[derive(Debug, Default, Clone)]
pub struct Fifa17CardCatalog {
by_card: HashMap<String, Fifa17CardIdentity>,
/// Reverse index: full versioned `resource_id` → the card definition id. The
/// wire carries a `resourceId`; the synthetic market must mint the
/// authoritative Core `card_id`, never the raw FIFA number.
by_resource: HashMap<u32, String>,
}
impl Fifa17CardCatalog {
@@ -168,7 +172,10 @@ impl Fifa17CardCatalog {
},
);
}
Ok(Fifa17CardCatalog { by_card })
Ok(Fifa17CardCatalog {
by_card,
by_resource,
})
}
/// Load a catalog from a JSON file.
@@ -183,6 +190,13 @@ impl Fifa17CardCatalog {
self.by_card.get(card_id).copied()
}
/// Reverse a FIFA wire `resource_id` (full versioned id) to its authoritative
/// Core `card_id`, or `None` (never a fabricated/heuristic id). Used by the
/// synthetic transfer market so a purchase mints real Core content.
pub fn card_id_for_resource(&self, resource_id: u32) -> Option<&str> {
self.by_resource.get(&resource_id).map(String::as_str)
}
pub fn len(&self) -> usize {
self.by_card.len()
}