diff --git a/docs/FIFA17_TRANSFER_MARKET_WIRE.md b/docs/FIFA17_TRANSFER_MARKET_WIRE.md index a321a43..c4e2244 100644 --- a/docs/FIFA17_TRANSFER_MARKET_WIRE.md +++ b/docs/FIFA17_TRANSFER_MARKET_WIRE.md @@ -653,7 +653,7 @@ row `1000000155` (res 158023, 93 RW). | `forSale` is FIFA 17-native, value 5 | **CONFIRMED** (12-row table at `0x180229cc0`) | | An expired row carrying `forSale` is still ACTIONABLE, and Return to Club works | **LIVE-CONFIRMED** — operator returned the card; server verified below | | The result is durable (a fresh session reconstructs it) | **CONFIRMED server-side**: `/tradePile` `total:0`, `/tradePile/counts` all zero, card present in `/club` (1965 → 1966 items, `itemState:"free"`), zero duplicate ids, listing `cancelled` in the store and projected as `closed` (4) on `/trade/status` | -| An ACTIVE seller row carrying `forSale` renders correctly | **NOT YET OBSERVED** — no active listing existed during the session; `/transfermarket` has always emitted `forSale` on active rows, so this is expected-safe but unverified | +| An ACTIVE seller row carrying `forSale` renders correctly | **LIVE-CONFIRMED** (Gap 1, 2026-08-17) — see the closure section below; the client decoded `CARD_OFFERSTATE = 5` out of its own auction record | | `CARD_OFFERSTATE` is the Flash action-gate term | **STILL UNPROVEN** — this change does not test it, and the result does not bear on it | What this rules out: `CARD_OFFERSTATE` is not a gate term that *requires* −1. An expired @@ -689,6 +689,74 @@ revert and go to the AVM1 gate work — NOT to compensate with another field cha the Flash decision tree; `STATE`, `CARD_OFFERSTATE` and `RESERVEDPRICE`/`MAX_CREDITS` remain confounded. +### Gap 1 CLOSED — an ACTIVE seller row under `forSale` (2026-08-17) + +The one thing the Fix B promotion could not test, because no active listing existed +during that session. Closed with a deliberately disposable card: a **75 ST**, resourceId +`212188`, wire id `100000178` — one of **three identical owned copies**, not in the +11-man squad, `untradeable: false`, discardValue 600. Listed **through the real FIFA 17 +client** at Start 150 / Buy Now 200 for the shortest offered duration (1h), so expiry +would arrive naturally. No timestamp was touched. + +Listing creation was entirely Rust-owned: +`PUT …/item` (club → pile) → `route=market-list POST item_id=100000178 listed=true +trade_id=1000000178` → `POST …/auctionhouse 200`, then the client polling +`GET …/trade/status?tradeIds=1000000178` (`requested=1 returned=1`) for the row it draws. + +Wire, active row `1000000178` +(`docs/evidence/market-lifecycle-2026-08-17/gap1-active-forsale.json`): + +| Field | Value | +|---|---| +| `itemState` | **`forSale`** | +| `tradeState` | `active` | +| `startingBid` / `buyNowPrice` / `currentBid` | 150 / 200 / 0 | +| `bidState` | `none` | +| `expires` | **3562 → 3556** over a 6-second sample — a live clock, not a frozen scalar | +| atom count | **12**, exactly what `0x18013e410` reads | +| `itemData` | full card, 6-entry `attributeList` | +| `/tradePile/counts` | `count 1, selling 1, offered 0, sold 0` | +| coins | 29,843,976 unchanged | +| Fix A | 0 `inactive` rows | + +`/trade/status` stayed coherent with `/tradePile` throughout, agreeing on tradeId, +`active`, `forSale` and a decreasing `expires`. + +**The decisive observation** is not the wire — it is what the client made of it. A +read-only `/proc//mem` decode of the live trade-pile auction record (slide proven by +FNV control MATCH first; `O_RDONLY` + `pread` only) returned: + +```text +tradeId=1000000178 state=1(active) bid=0(none) buyNow=200 start=150 cur=0 expires=3457 +item CARD_ID=212188 cardType=1 tradeable=1 itemState=5(forSale) pile=0 rating=75 +``` + +`itemState = 5(forSale)`. The same field read `-1()` under `listFS`. This is +the direct A/B on the only changed field, taken from the client's own memory rather than +from our response — the strongest form of this evidence available without AVM1 work. +`pile = 0` again, confirming pile is still never set from the wire. + +Operator UI acceptance: card under **LISTED ITEMS** (not the TRANSFER LIST section), Start +150 / Buy Now 200 correct, **Time Remaining visibly counting down**, normal card art, no +malformed row, no duplicate, no premature Expired, no crash, and correctly +**non-actionable while active**. The client's container populating with exactly this row +(`rows=1`) independently corroborates that the Transfer List screen bound and drew it. + +**No code change was required** — the promoted implementation was already correct on the +active path, which is what the test was designed to find out. + +Two process notes worth keeping. First, an early capture reported "countdown NO"; that was +a bug in the capture script (it re-read a hardcoded copy id rather than the row it had +resolved), not a server fault — the same run's `/trade/status` already showed a lower +`expires` than `/tradePile`. Verify the instrument before believing a negative. Second, +the client's auction-record containers read **0 rows** while the operator was on any other +screen; they populate only while the Transfer List is bound. An empty container is not +evidence of an empty pile. + +**Claim boundary unchanged:** this proves the client *decodes* the token. It says nothing +about the Flash action-gate term. + + ### Proving the gate (not yet done) Disassemble the AVM1 action buffer of `tradepile.isInActiveAuction` (and diff --git a/docs/evidence/market-lifecycle-2026-08-17/gap1-active-forsale.json b/docs/evidence/market-lifecycle-2026-08-17/gap1-active-forsale.json new file mode 100644 index 0000000..4089763 --- /dev/null +++ b/docs/evidence/market-lifecycle-2026-08-17/gap1-active-forsale.json @@ -0,0 +1,173 @@ +{ + "gap": 1, + "test": "ACTIVE seller row under itemState=forSale", + "date": "2026-08-17", + "disposable_card": { + "wire_item_id": 100000178, + "resourceId": 212188, + "rating": 75, + "preferredPosition": "ST", + "copies_owned": 3, + "discardValue": 600, + "in_active_squad": false, + "untradeable": false + }, + "pre_state": { + "club_items": 1966, + "coins": 29843976, + "tradePile_rows": 0, + "tradePile_counts": { + "count": 0, + "maxAuctionsAllowed": 100, + "offered": 0, + "selling": 0, + "sold": 0 + } + }, + "listing_creation_route_log": "utas-host owner=RUST route=market-list POST item_id=100000178 listed=true trade_id=1000000178", + "client_flow_observed_rust_owned": [ + "PUT /ut/game/fifa17/item (club -> trade pile move)", + "POST /ut/game/fifa17/auctionhouse (ISStart) -> 200", + "GET /ut/game/fifa17/tradePile (repeated)", + "GET /ut/game/fifa17/trade/status?tradeIds=1000000178 -> requested=1 returned=1 (repeated poll)", + "GET /ut/game/fifa17/tradePile/counts" + ], + "wire_active_row": { + "tradeId": 1000000178, + "tradeState": "active", + "itemState": "forSale", + "startingBid": 150, + "buyNowPrice": 200, + "currentBid": 0, + "bidState": "none", + "sellerName": "CAGE", + "sellerEstablished": 1, + "watched": false, + "coinsProcessed": 0, + "atom_count": 12, + "itemData_attributeList_entries": 6 + }, + "countdown_sample": { + "expires": [ + 3562, + 3556 + ], + "delta_secs": 6, + "live": true + }, + "trade_status_coherent_while_running": true, + "trade_status_rows": [ + { + "bidState": "none", + "buyNowPrice": 200, + "coinsProcessed": 0, + "currentBid": 0, + "expires": 3556, + "itemData": { + "assetId": 212188, + "attributeList": [ + { + "index": 0, + "value": 90 + }, + { + "index": 1, + "value": 70 + }, + { + "index": 2, + "value": 64 + }, + { + "index": 3, + "value": 78 + }, + { + "index": 4, + "value": 24 + }, + { + "index": 5, + "value": 62 + } + ], + "cardassetid": 212188, + "cardsubtypeid": 0, + "contract": 7, + "definitionId": 212188, + "discardValue": 600, + "fitness": 99, + "id": 100000178, + "itemState": "forSale", + "itemType": "player", + "leagueId": 19, + "nation": 21, + "owners": 1, + "playStyle": 250, + "preferredPosition": "ST", + "rareflag": 1, + "rating": 75, + "resourceId": 212188, + "teamid": 112172, + "untradeable": false + }, + "sellerEstablished": 1, + "sellerName": "CAGE", + "startingBid": 150, + "tradeId": 1000000178, + "tradeState": "active", + "watched": false + } + ], + "tradePile_counts_while_active": { + "count": 1, + "maxAuctionsAllowed": 100, + "offered": 0, + "selling": 1, + "sold": 0 + }, + "coins_unchanged": true, + "fix_a_inactive_rows": 0, + "CARD_OFFERSTATE_live_decode": { + "observed": true, + "method": "read-only /proc//mem via fifa17-recon-derived auction_record_probe.py; slide proven by FNV control MATCH before any other read; O_RDONLY + pread only", + "client_pid": 29670, + "container": "model->vt[0x120] (trade pile), rows=1, +0x60 override=1", + "record": { + "tradeId": 1000000178, + "STATE_tradeState": "1(active)", + "YOURBID_bidState": "0(none)", + "MAX_CREDITS_buyNowPrice": 200, + "RESERVEDPRICE_startingBid": 150, + "currentBid": 0, + "expires": 3457, + "coinsProcessed": 0, + "IS_GLOW_flagB": 0, + "INBOX_flagA": 0, + "IS_WATCHED": 0 + }, + "item": { + "CARD_ID": 212188, + "CARD_TYPE": 1, + "tradeable": 1, + "rating": 75, + "CARD_OFFERSTATE_itemState": "5(forSale)", + "pile": 0 + }, + "ab_significance": "This is the direct A/B on the only changed field. Baseline `listFS` decoded to -1() in the client's own record; candidate `forSale` decodes to 5. Every other field in the record is unchanged in kind.", + "claim_boundary": "Proves the CLIENT DECODES the token correctly. Says nothing about the Flash action-gate term." + }, + "ui_acceptance": { + "operator_report": "looks right", + "confirmed": [ + "card renders under LISTED ITEMS (not the TRANSFER LIST section)", + "Start Price 150 and Buy Now 200 displayed correctly, Current Bid as expected", + "Time Remaining visibly counting down", + "card art renders normally; no malformed row, no duplicate, no premature Expired, no crash", + "row correctly non-actionable while active (established FIFA 17 behaviour, not a fault)" + ], + "corroboration": "the client's trade-pile container populated with exactly this row (rows=1), so the Transfer List screen bound and drew it" + }, + "verdict": "GAP 1 CLOSED \u2014 LIVE-CONFIRMED: an active seller auction with itemState=forSale renders correctly and is decoded by the client as CARD_OFFERSTATE=5", + "code_change_required": false +}