Commit Graph

8 Commits

Author SHA1 Message Date
OpenFUT Agent 43917a0051 feat(fifa17): attach economy services in Server::from_config
Wire the PRODUCTION constructor so the economy authority is not test-only.
Server::from_config now builds one process-lifetime AsyncBridge, opens the
durable MarketStore + PileStore (paths from config), shares one HttpCoreClient
as both CoreAccess and CoreEconomy, builds the content pool from Core, and
attaches EconomyServices via with_economy. Stores/bridge are host-lifetime, never
per request.

- config.rs: required OPENFUT_MARKET_DB / OPENFUT_PILE_DB (durable file paths;
  must survive host restart — no temp defaults).
- Fail-closed startup: a bridge/store that cannot initialize returns Err from
  from_config (host refuses to start) — NEVER a silent omission or a Python
  economy fallback.

Test: from_config_constructs_and_serves_economy — builds the Server via the REAL
from_config (disposable config: temp market/pile/identity + a catalog file
derived from seeded content + the real tables dir) against a live Core, drives
credits / purchasegroup / Store BUY / market list-query-buy through it, then
rebuilds from the SAME config after a Core restart and asserts the balance
persisted. host 71 lib + 3 integration + 24 host_test green; clippy/fmt clean.
2026-08-13 21:59:29 +00:00
OpenFUT Agent 747cc234c1 fix(fifa17): preserve owned-pack reveal state for GET /purchased
Closes the reveal contract gap: POST /purchased opens a pack and returns
metadata; the client then polls GET /purchased for the opened items. Store BUY
returns items inline, but owned reward-pack (e.g. pack 70) opens had no reveal
read path, so a real FIFA session would show nothing after opening.

Faithful to the Python oracle (fut_store.last_pack / purchased pile): the reveal
is the set of owned items currently in the FIFA "purchased" pile — durable,
idempotent on repeat GET, cleared per-item when a card is moved to the club, and
appended-to by each open. Not a replay cache; presentation state derived from
the durable pile store + Core inventory.

- pile_store.rs: `list_by_pile(pile) -> Vec<core_item_id>` (reveal membership).
- economy_store.rs: `PurchasedPileSink` trait + optional `StoreDeps.purchased`;
  handle_store_buy/handle_pack_open record each minted item into the "purchased"
  pile. `shape_purchased_reveal` (pure): filter Core inventory to the purchased
  pile, shape with the SAME `shape_club_response` /club uses. Grants nothing,
  consumes no entitlement, allocates no id, moves no coins.
- lib.rs: EconomyRoute::PackReveal + classify_economy (GET purchased);
  BridgedPurchasedSink (records via the runtime bridge from the sync dispatch
  thread); dispatch reads the pile async + Core inventory sync + pure-shapes.

Scoping: single fifa17 profile/club (like the Python oracle), so all sessions
share one purchased pile — DIFFERENT-BY-DESIGN vs a per-SID cache, matching the
oracle's single-profile model.

Tests: pile_store::list_by_pile_filters_and_reflects_moves; and the dispatch E2E
now opens pack 70 (entitlement seeded via the Core economy API) and asserts GET
/purchased reveals the opened items and is idempotent on repeat. host 71 lib +
2 integration + 24 host_test green; clippy -D warnings + fmt clean.
2026-08-13 21:51:12 +00:00
OpenFUT Agent fe72f0def2 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.
2026-08-13 21:43:48 +00:00
OpenFUT Agent 580d80a86e feat(fifa17): wire async economy handlers into the host via a runtime bridge (unrouted)
Bridges the synchronous thread-per-connection host to the async
transfer-market/pile handlers WITHOUT flipping the classifier. classify()
is untouched; production still proxies every economy route to Python. The
new dispatch is exercised only by the integration harness via
Server::try_handle_economy — handler wiring, not authority cutover.

async_bridge.rs: AsyncBridge owns ONE process-lifetime multi-threaded Tokio
runtime, shared by every connection via Arc. block_on() runs a future from
the sync dispatch thread; if invoked from within an ambient runtime it
offloads onto its own runtime + a std channel instead of panicking
("cannot start a runtime from within a runtime"). 4 unit tests incl. the
nested-runtime-safety case and concurrent multi-thread drivers.

lib.rs: EconomyRoute + classify_economy (mirrors the Python route table:
credits, purchasegroup, store/transaction, purchased, item DELETE/PUT,
ut/delete match/item/trade, auctionhouse/transfermarket, tradePile, trade).
EconomyServices (Core econ transport + durable MarketStore/PileStore + the
bridge + the pack-content pool), attached via Server::with_economy (kept out
of `new`/`from_config` so existing tests build a DB-less Server; production
from_config attachment is the barrier step). Server::try_handle_economy
dispatches: sync handlers (credits/purchasegroup/store-buy/pack-open/
quick-sell/match) inline; async handlers (market list/query/buy/cancel,
move) on the bridge via owned `async move` blocks. build_content_pool
derives the resolvable FIFA∩Core candidate pool from Core content.

market.rs: FIX the load-bearing hazard the FakeEconomy tests missed — the
async market handlers call the BLOCKING reqwest Core client, which panics
(reqwest::blocking::wait::enter) when run while a Tokio runtime is entered.
off_runtime() hops each Core call to a fresh OS thread with no runtime
entered, so blocking is legal. handle_move_items resolver gains `+ Sync`
(future must be Send for the bridge).

tests/economy_integration.rs: economy_full_sequence_through_dispatch drives
the WHOLE cluster through the REAL Server dispatch + bridge against a live
in-process Core (seeded with fifa17 dev content: 100k coins + owned cards),
on a plain OS thread (direct bridge path), over the real blocking
HttpCoreClient — no fakes: Store BUY (pool draw + shape + debit 400 + mint 5),
credits, quick-sell (reverse-resolve + credit), match WIN (+400), market
list->query->buy->query(sold)->second-buy-fails(no double debit), cancel
(cancelled not buyable), move-items, then reopen the durable market/pile
stores from disk (sold + pile persist). Deterministic. start_core_seeded
loads fifa17 dev content so /collection renders real definitions.

Tests: host 68 lib (+4 bridge) + 2 economy_integration + 24 host_test, all
green; adapter unchanged-green; clippy -D warnings + fmt clean.
2026-08-13 21:30:10 +00:00
OpenFUT Agent 0b31abe1d1 test(fifa17): run economy harness on a real multi-connection Core pool
The fresh-DB write-lock race is fixed (core fbb54ea: BEGIN IMMEDIATE writes),
so the E2E+restart harness now uses max_connections=5; 10/10 deterministic.
2026-08-13 20:20:58 +00:00
OpenFUT Agent 4f78b9a875 test(fifa17): keep economy harness serialized; document multi-conn blocker
WAL-establish-once + busy_timeout (core 75b1830) reduced but did not eliminate a
brand-new-DB multi-connection warm-up 'database error'; the E2E+restart harness
stays on a single serialized connection for determinism, and the residual
multi-connection concurrency issue is documented as the remaining Part-T blocker.
2026-08-13 20:05:34 +00:00
OpenFUT Agent 1df0bc4f00 test(fifa17): make economy integration harness deterministic
Serialize Core access with a single pooled connection (the harness drives Core
sequentially via the blocking client) to avoid a WAL-mode-establishment race
across connections warming up on a brand-new DB file, and raise the readiness
ceiling for heavy parallel test-binary load. 10/10 deterministic. (Fixed
alongside a real Core robustness fix: per-connection pragmas + busy_timeout,
core 0360135.)
2026-08-13 19:57:53 +00:00
OpenFUT Agent 8d752cb0e4 test(fifa17): real host<->Core economy integration harness
Spawn Core (axum) on an ephemeral loopback port backed by a disposable temp-file
SQLite, seed a fifa17 profile via the real Core HTTP API, then drive the HOST's
REAL transport (HttpCoreClient: CoreEconomy) + handlers against it — no fakes:
credits reads Core balance; match-reward writer credits via Core grant_reward;
purchasegroup full-gen renders the owned pack from a Core entitlement (no
sentinel); userMassInfo overlay derives coins from the same Core state
(credits==massinfo==Core invariant). Restart phase reboots Core from the same
on-disk DB and proves coins + entitlements persist. Temp dir + 127.0.0.1:0 only;
no prod DB/ports/containers/.105. dev-deps: openfut-core, tokio, axum.
2026-08-13 19:50:42 +00:00