fix(market): /trade/status must resolve the unlisted ids /tradePile advertises
Explains and fixes the Phase C partial failure WITHOUT changing a single wire field. The operator saw a difference between the one-item probe (Time Remaining "-") and the generalized rows (Time Remaining "Expired"). Cause: route coverage, not encoding. /tradePile advertised the unlisted tradeIds while ISVIEWTRADE (GET .../trade/status) resolved ids from the market store only -- and an unlisted pile member has no listing row, so the poll returned an empty auctionInfo. Observed live as `route=market-status requested=1 returned=0` repeating for the row the operator had selected, while that same id was present in /tradePile. The client polls status for the row it displays and degrades it when the answer is empty, which is also why no actions were offered. The probe showed "-" only because the client had not yet polled that id (logs of the time show only tradeIds=1000000097). So expires, tradeState, itemData.itemState and pile were all innocent. Nothing was guessed and no field changed: both routes now share one pile enumeration (Server::resolve_trade_pile), so an id advertised by /tradePile always resolves on /trade/status. The corpus predicted exactly this -- tradeId must resolve across /transfermarket, /tradePile, /watchList AND /trade/status; we had stability but not coverage. Same defect class as the original empty-trade/status bug. Status still answers only the ids actually asked about, and a real auction always wins over an inactive row for the same tradeId. Regression test covers all four cases. Records the downgraded conclusion: "inactive" is a CONFIRMED section/lifecycle discriminator; whether the full actionable contract is now complete is the operator's next test. itemState/pile recovery was queued on the assumption the encoding was incomplete -- neither was touched, and both remain the next candidates if actions are still absent. 342 tests pass, 0 failed, clippy clean. Verified live: the six inactive ids went from returned=0 to returned=6.
This commit is contained in:
@@ -531,3 +531,60 @@ So `count` currently tracks AUCTION entries, not total Transfer List membership.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Q2 status downgrade + the "Expired" differential EXPLAINED (2026-08-17)
|
||||
|
||||
Phase C was a PARTIAL pass. Corrected conclusions:
|
||||
|
||||
```text
|
||||
tradeState "inactive" = CONFIRMED section/lifecycle discriminator
|
||||
(rows persist across a fresh session and land under
|
||||
TRANSFER LIST, not LISTED ITEMS)
|
||||
|
||||
complete ACTIONABLE unlisted representation = was UNKNOWN; see below
|
||||
```
|
||||
|
||||
### The visual differential, and its cause
|
||||
|
||||
| | one-item probe | generalized rows |
|
||||
|---|---|---|
|
||||
| Start Price | 0 | 0 |
|
||||
| Buy Now / Current Bid | `-` | `-` |
|
||||
| Time Remaining | `-` | **`Expired`** |
|
||||
|
||||
Cause: **route coverage, not a field.** `/tradePile` advertised the unlisted
|
||||
tradeIds, but `GET …/trade/status` (ISVIEWTRADE) answered them from the market store
|
||||
ONLY — and an unlisted pile member has no listing row, so the poll returned an empty
|
||||
`auctionInfo`. Observed live as `route=market-status requested=1 returned=0` repeating
|
||||
for the row the operator had selected (`tradeIds=1000000122`), while the same id was
|
||||
present in `/tradePile`.
|
||||
|
||||
The client polls `/trade/status` for the row it is displaying, and an empty answer
|
||||
degrades it: Time Remaining renders `Expired` and no actions are offered. The earlier
|
||||
probe showed `-` simply because the client had not yet polled that id — the logs at
|
||||
the time show only `tradeIds=1000000097`. So `expires`, `tradeState`,
|
||||
`itemData.itemState` and `pile` were all innocent, and NO field was changed.
|
||||
|
||||
This is the same defect class as the original `trade/status` bug: a route the client
|
||||
polls being answered with an empty set. The corpus predicted it — `tradeId` must
|
||||
resolve across `/transfermarket`, `/tradePile`, `/watchList` AND `/trade/status`. We
|
||||
had stability but not coverage.
|
||||
|
||||
Fix: both routes now share ONE pile enumeration (`Server::resolve_trade_pile`), so an
|
||||
id advertised by `/tradePile` always resolves on `/trade/status`. Verified live: the
|
||||
six inactive ids went from `returned=0` to `returned=6`. `/trade/status` still answers
|
||||
only the ids actually asked about, and a real auction always wins over an inactive row
|
||||
for the same id.
|
||||
|
||||
Regression test: `trade_status_resolves_the_unlisted_ids_tradepile_advertises`.
|
||||
|
||||
### Investigations NOT needed as a result
|
||||
|
||||
`itemData.itemState` and numeric `itemData.pile` were queued for PE recovery on the
|
||||
assumption the encoding was incomplete. The cause was route coverage, so neither was
|
||||
touched and neither is implicated. If the actions still do not appear, those remain
|
||||
the next candidates — and `itemState`'s vocabulary should be dumped statically with
|
||||
`fifa17-recon/tools/vocab_dump.py` (already validated against the tradeState table)
|
||||
rather than guessed.
|
||||
|
||||
Reference in New Issue
Block a user