docs: record the LIVE-CONFIRMED inactive UI contract and the expired->Club transition
Operator confirmed in the real client that a tradeState "inactive" row lands under the right-hand TRANSFER LIST section and renders Start Price 0 with Buy Now, Current Bid and Time Remaining all absent, while an active row in the same body continued to render separately under LISTED ITEMS. That is the Q2 representation confirmed live, with the token itself dumped from the client's own string table rather than guessed. Records the observed wire->UI contract as a table plus a fixture (inactive-row-live-confirmed.json), and names the regression tests that pin it, including the two guards that the row is never emitted for an item outside the trade pile and never duplicates a real auction. Also records the expired->Club coupled transition verified server-side: the listing went to `cancelled`, the pile went to `club`, /tradePile dropped the item, /club regained it, and clubPlayers went 1964 -> 1965. That is durable store state rather than a client-local view, so it survives a session boundary by construction; tagged pending the operator's final exit/re-enter confirmation. Adds the counts observation table. `count` currently tracks AUCTION entries and not total Transfer List membership; semantics deliberately left unchanged until the full state set has been observed. No behaviour change in this commit.
This commit is contained in:
@@ -466,3 +466,68 @@ state and auction lifecycle are separate authoritative facts; the wire may expos
|
||||
through different FIFA 17 resources. **A transition is complete only when a fresh FIFA
|
||||
session can reconstruct the same user-visible state** — re-entry is the acceptance
|
||||
test, not the immediate post-mutation response.
|
||||
|
||||
---
|
||||
|
||||
## Q2 — LIVE-CONFIRMED: `tradeState: "inactive"` is the unlisted representation
|
||||
|
||||
Operator-observed in the real FIFA 17 client, 2026-08-17, with the bounded one-item
|
||||
probe (`OPENFUT_FIFA17_UNLISTED_PROBE`). Fixture:
|
||||
`docs/evidence/market-lifecycle-2026-08-17/inactive-row-live-confirmed.json`.
|
||||
|
||||
### The observed `inactive` UI contract
|
||||
|
||||
| Wire | Client presentation |
|
||||
|---|---|
|
||||
| `tradeState: "inactive"` | row placed under the right-hand **TRANSFER LIST** section, NOT under LISTED ITEMS |
|
||||
| `startingBid: 0` | Start Price = `0` |
|
||||
| `buyNowPrice: 0` | Buy Now Price = `-` (absent) |
|
||||
| `currentBid: 0` | Current Bid = `-` (absent) |
|
||||
| `expires: 0` | Time Remaining = `-` (no timer) |
|
||||
| `bidState: "none"` | no bid presentation |
|
||||
| `itemData.itemState: "free"` | renders as a normal owned card |
|
||||
|
||||
An `active` row in the same body continued to render separately under LISTED ITEMS,
|
||||
so the two sections are driven by `tradeState` and the experiment did not disturb the
|
||||
known-good active path.
|
||||
|
||||
Token provenance: dumped from the client's own `{const char*, int}` table at
|
||||
`0x180229e40` (`active=1 inactive=2 expired=3 closed=4`) — see
|
||||
`cardsdll-vocab-tables.txt` and `fifa17-recon/tools/vocab_dump.py`. Nothing here was
|
||||
guessed.
|
||||
|
||||
Regression test: `market::tests::unlisted_candidate_is_a_non_active_pile_row` pins the
|
||||
whole tuple, plus that the row is never emitted for an item outside the `trade` pile
|
||||
and never duplicates a real auction
|
||||
(`unlisted_candidate_never_duplicates_a_real_auction`).
|
||||
|
||||
### Expired -> Club coupled transition, verified
|
||||
|
||||
Operator returned the naturally-expired Ronaldo to the club. Server state after:
|
||||
|
||||
```
|
||||
listing 1000000097 state = cancelled (the coupled transition fired)
|
||||
pile Ronaldo = club
|
||||
/tradePile Ronaldo absent
|
||||
/club Ronaldo present, itemState free
|
||||
hub clubPlayers 1964 -> 1965, auctionCount 1
|
||||
counts {count 1, selling 1, sold 0}
|
||||
```
|
||||
|
||||
All of that is durable store state rather than a client-local view, so it survives a
|
||||
session boundary by construction. Pending the operator's final exit/re-enter
|
||||
confirmation before this is tagged CONFIRMED.
|
||||
|
||||
### Counts observations so far (semantics still UNCHANGED and unresolved)
|
||||
|
||||
| State | `count` | `selling` | `sold` |
|
||||
|---|---|---|---|
|
||||
| 1 active | 1 | 1 | 0 |
|
||||
| 2 active | 2 | 2 | 0 |
|
||||
| 1 active + 1 inactive (probe) | 1 | 1 | 0 |
|
||||
| unlisted pile items (no probe) | not counted | not counted | 0 |
|
||||
|
||||
So `count` currently tracks AUCTION entries, not total Transfer List membership. Do
|
||||
not change this until the full state set (empty / unlisted / active / expired / sold /
|
||||
mixed) has been observed — it remains an open question whether FIFA 17 expects
|
||||
`count` to include non-active pile members.
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"auctionInfo": [
|
||||
{
|
||||
"bidState": "none",
|
||||
"buyNowPrice": 15000,
|
||||
"coinsProcessed": 0,
|
||||
"currentBid": 0,
|
||||
"expires": 2674,
|
||||
"itemData": {
|
||||
"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": 100000155,
|
||||
"itemState": "listFS",
|
||||
"itemType": "player",
|
||||
"leagueId": 53,
|
||||
"nation": 52,
|
||||
"owners": 1,
|
||||
"playStyle": 250,
|
||||
"preferredPosition": "RW",
|
||||
"rareflag": 1,
|
||||
"rating": 93,
|
||||
"resourceId": 158023,
|
||||
"teamid": 241,
|
||||
"untradeable": false
|
||||
},
|
||||
"sellerEstablished": 1,
|
||||
"sellerName": "CAGE",
|
||||
"startingBid": 150,
|
||||
"tradeId": 1000000155,
|
||||
"tradeState": "active",
|
||||
"watched": false
|
||||
},
|
||||
{
|
||||
"bidState": "none",
|
||||
"buyNowPrice": 0,
|
||||
"coinsProcessed": 0,
|
||||
"currentBid": 0,
|
||||
"expires": 0,
|
||||
"itemData": {
|
||||
"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
|
||||
},
|
||||
"sellerEstablished": 1,
|
||||
"sellerName": "CAGE",
|
||||
"startingBid": 0,
|
||||
"tradeId": 1000000059,
|
||||
"tradeState": "inactive",
|
||||
"watched": false
|
||||
}
|
||||
],
|
||||
"credits": 29843976,
|
||||
"duplicateItemIdList": [],
|
||||
"total": 2
|
||||
}
|
||||
Reference in New Issue
Block a user