diff --git a/docs/FIFA17_TRANSFER_MARKET_WIRE.md b/docs/FIFA17_TRANSFER_MARKET_WIRE.md index c62c242..595d961 100644 --- a/docs/FIFA17_TRANSFER_MARKET_WIRE.md +++ b/docs/FIFA17_TRANSFER_MARKET_WIRE.md @@ -330,3 +330,69 @@ pile reads `club` while the listing row stays `active`, so the card is filtered of `/club` (exclusion keys on active listings) AND still rendered in the Transfer List — the move silently appears to do nothing. `reserved` (mid-sale) and `sold` rows are never touched, so a card can never be both sold and returned. + +--- + +## Q2 — the unlisted transfer-list item, MEASURED 2026-08-17 + +The operator moved a card Club -> Transfer List **without listing it** +(`PUT /ut/game/fifa17/item`, no `POST /auctionhouse`). State captured immediately, +read-only. Fixture: `docs/evidence/market-lifecycle-2026-08-17/`. + +Server truth at capture: trade pile held **6** items, of which **1** had an active +auction and **5** were unlisted. + +| Surface | Unlisted item | Actively-listed item (contrast) | +|---|---|---| +| `/tradePile` `auctionInfo` | **absent** | present, `tradeState: active` | +| `/tradePile/counts` | **not counted** (`count 1, selling 1`) | counted | +| `/club` | **present**, `clubPlayers` unchanged at 1965 | **absent** (excluded) | +| `itemData.itemState` | `free` | `listFS` | +| `itemData.pile` | **field not emitted at all** | not emitted | +| `itemData.untradeable` | `false` | `false` | + +**Finding: we do not represent the unlisted state anywhere on the wire.** Such an +item is byte-identical to a club item; only the host's own pile store knows it is in +the trade pile, and nothing the client receives says so. + +Still **UNKNOWN** (do not guess): the FIFA 17 encoding of that state. The PE +documents returned `itemData.pile` as NUMERIC with an unrecovered mapping, and +`tradeState` decodes through a closed table walk (`active=1 inactive=2 expired=3 +closed=4`) where an unrecognised `bidState` is silently swallowed as `none` — so a +wrong enum yields a plausible-looking but WRONG UI. The corpus explicitly warns that +`inactive` decodes but "no client path treats it specially; do not emit it". + +Open discriminator, needs the client only: after backing out of FUT and re-entering, +does the Transfer List still show an unlisted item? If it does not, the move is not +durable from the client's point of view and the state must be represented; if it +does, `tradePile` is auctions-only by design and there is nothing to fix. + +--- + +## Domain boundary: pile membership vs auction lifecycle + +Two SEPARATE facts, but several mutations require a COORDINATED transition. Both +limbo bugs this session came from conflating them, so this is modelled explicitly +and covered by tests rather than patched route by route. + +| Mutation | Pile | Auction | +|---|---|---| +| Club -> Transfer List | becomes `trade` | none necessarily exists | +| list item | stays `trade` | becomes `active` | +| clock runs out unsold | stays `trade` | becomes `expired` (projection; no row mutated) | +| relist | stays `trade` | `active` again, clock restarted, new prices | +| return expired item to Club | becomes `club` | any `active` association MUST end | +| sold / reserved | — | MUST NOT be ended by a generic move-to-club | + +Invariant, stated so it can be tested rather than remembered: **an item must never be +simultaneously excluded from `/club` and absent from `/tradePile`.** That is the limbo +state, and it is reachable in two ways, both now closed: + +1. Excluding by *pile* rather than by *active listing* (fixed in `f2c4927`) — the + pile can hold items with no auction, and `/tradePile` renders auctions only. +2. Moving to `club` while leaving the auction `active` (fixed in `4e31fb9`) — the + pile says club, the exclusion still fires, and the card shows in neither place. + +Tests: `returning_an_expired_listing_to_the_club_ends_its_auction`, +`a_pile_move_never_disturbs_a_sale_in_flight`, +`club_excludes_listed_items_and_paginates_the_visible_set`. diff --git a/docs/evidence/market-lifecycle-2026-08-17/hub.json b/docs/evidence/market-lifecycle-2026-08-17/hub.json new file mode 100644 index 0000000..e85ca20 --- /dev/null +++ b/docs/evidence/market-lifecycle-2026-08-17/hub.json @@ -0,0 +1,9 @@ +{ + "auctionCount": 1, + "clubPlayers": 1965, + "tradePile": { + "count": 1, + "selling": 1, + "sold": 0 + } +} diff --git a/docs/evidence/market-lifecycle-2026-08-17/trade-status-unfiltered.json b/docs/evidence/market-lifecycle-2026-08-17/trade-status-unfiltered.json index 55d702b..97114c2 100644 --- a/docs/evidence/market-lifecycle-2026-08-17/trade-status-unfiltered.json +++ b/docs/evidence/market-lifecycle-2026-08-17/trade-status-unfiltered.json @@ -5,7 +5,7 @@ "buyNowPrice": 15000, "coinsProcessed": 0, "currentBid": 0, - "expires": 2220, + "expires": 1199, "itemData": { "assetId": 20801, "attributeList": [ diff --git a/docs/evidence/market-lifecycle-2026-08-17/tradePile.json b/docs/evidence/market-lifecycle-2026-08-17/tradePile.json index ead56c0..d9e01ec 100644 --- a/docs/evidence/market-lifecycle-2026-08-17/tradePile.json +++ b/docs/evidence/market-lifecycle-2026-08-17/tradePile.json @@ -5,7 +5,7 @@ "buyNowPrice": 15000, "coinsProcessed": 0, "currentBid": 0, - "expires": 2220, + "expires": 1199, "itemData": { "assetId": 20801, "attributeList": [ diff --git a/docs/evidence/market-lifecycle-2026-08-17/unlisted-club-records.json b/docs/evidence/market-lifecycle-2026-08-17/unlisted-club-records.json new file mode 100644 index 0000000..46148e7 --- /dev/null +++ b/docs/evidence/market-lifecycle-2026-08-17/unlisted-club-records.json @@ -0,0 +1,242 @@ +{ + "100000059": { + "assetId": 158023, + "attributeList": [ + { + "index": 0, + "value": 89 + }, + { + "index": 1, + "value": 90 + }, + { + "index": 2, + "value": 86 + }, + { + "index": 3, + "value": 96 + }, + { + "index": 4, + "value": 26 + }, + { + "index": 5, + "value": 61 + } + ], + "cardassetid": 158023, + "cardsubtypeid": 0, + "contract": 7, + "definitionId": 158023, + "discardValue": 1500, + "fitness": 99, + "id": 100000059, + "itemState": "free", + "itemType": "player", + "leagueId": 53, + "nation": 52, + "owners": 1, + "playStyle": 250, + "preferredPosition": "RW", + "rareflag": 1, + "rating": 93, + "resourceId": 158023, + "teamid": 241, + "untradeable": false + }, + "100000122": { + "assetId": 177003, + "attributeList": [ + { + "index": 0, + "value": 74 + }, + { + "index": 1, + "value": 75 + }, + { + "index": 2, + "value": 86 + }, + { + "index": 3, + "value": 89 + }, + { + "index": 4, + "value": 72 + }, + { + "index": 5, + "value": 66 + } + ], + "cardassetid": 177003, + "cardsubtypeid": 0, + "contract": 7, + "definitionId": 177003, + "discardValue": 1500, + "fitness": 99, + "id": 100000122, + "itemState": "free", + "itemType": "player", + "leagueId": 53, + "nation": 10, + "owners": 1, + "playStyle": 250, + "preferredPosition": "CM", + "rareflag": 1, + "rating": 89, + "resourceId": 177003, + "teamid": 243, + "untradeable": false + }, + "100000127": { + "assetId": 177003, + "attributeList": [ + { + "index": 0, + "value": 74 + }, + { + "index": 1, + "value": 75 + }, + { + "index": 2, + "value": 86 + }, + { + "index": 3, + "value": 89 + }, + { + "index": 4, + "value": 72 + }, + { + "index": 5, + "value": 66 + } + ], + "cardassetid": 177003, + "cardsubtypeid": 0, + "contract": 7, + "definitionId": 177003, + "discardValue": 1500, + "fitness": 99, + "id": 100000127, + "itemState": "free", + "itemType": "player", + "leagueId": 53, + "nation": 10, + "owners": 1, + "playStyle": 250, + "preferredPosition": "CM", + "rareflag": 1, + "rating": 89, + "resourceId": 177003, + "teamid": 243, + "untradeable": false + }, + "100004866": { + "assetId": 163824, + "attributeList": [ + { + "index": 0, + "value": 70 + }, + { + "index": 1, + "value": 43 + }, + { + "index": 2, + "value": 56 + }, + { + "index": 3, + "value": 62 + }, + { + "index": 4, + "value": 82 + }, + { + "index": 5, + "value": 82 + } + ], + "cardassetid": 163824, + "cardsubtypeid": 0, + "contract": 7, + "definitionId": 163824, + "discardValue": 900, + "fitness": 99, + "id": 100004866, + "itemState": "free", + "itemType": "player", + "leagueId": 13, + "nation": 50, + "owners": 1, + "playStyle": 250, + "preferredPosition": "CB", + "rareflag": 1, + "rating": 83, + "resourceId": 163824, + "teamid": 7, + "untradeable": false + }, + "100004867": { + "assetId": 172610, + "attributeList": [ + { + "index": 0, + "value": 69 + }, + { + "index": 1, + "value": 33 + }, + { + "index": 2, + "value": 47 + }, + { + "index": 3, + "value": 57 + }, + { + "index": 4, + "value": 80 + }, + { + "index": 5, + "value": 76 + } + ], + "cardassetid": 172610, + "cardsubtypeid": 0, + "contract": 7, + "definitionId": 172610, + "discardValue": 900, + "fitness": 99, + "id": 100004867, + "itemState": "free", + "itemType": "player", + "leagueId": 31, + "nation": 54, + "owners": 1, + "playStyle": 250, + "preferredPosition": "CB", + "rareflag": 1, + "rating": 80, + "resourceId": 172610, + "teamid": 54, + "untradeable": false + } +}