docs(route-authority): record v1/v2 economy URL prefix contract

Accepted prefixes /ut/game/<sku>/ and /ut/v2/game/<sku>/ for every economy
route; StoreBuy accepts store/transaction and store/transaction/<txn-id>.
This commit is contained in:
OpenFUT Agent
2026-08-13 23:53:44 +00:00
parent b8beeba98d
commit 47ced228de
+32
View File
@@ -12,6 +12,38 @@ entitlements (`unopenedPackIds`). `points` has **no** writer (read-only). EASFC
Legend: **R** = Rust/Core authoritative, **P** = Python proxied (oracle). Legend: **R** = Rust/Core authoritative, **P** = Python proxied (oracle).
Evidence lines refer to `fifa17-recon/tools/{utas_server.py,fut_store.py}`. Evidence lines refer to `fifa17-recon/tools/{utas_server.py,fut_store.py}`.
## Accepted URL prefixes (v1 + v2) — S2 fix
The retail FIFA 17 client issues the **Store family** under a `/ut/v2/game/<sku>/`
prefix (live-observed `PUT /ut/v2/game/fifa17/store/transaction/0`), while other
routes use `/ut/game/<sku>/`. `classify_economy` normalizes BOTH prefixes to the
same tail (`ut_tail`), so economy ownership is prefix-agnostic. This closes the
S2 live-staging defect where the v2 Store BUY escaped to Python.
| Route | Accepted method + path shapes (both prefixes) | Economy route |
|---|---|---|
| Credits | `GET (/ut/game\|/ut/v2/game)/<sku>/user/credits` | `Credits` |
| Store catalogue | `GET …/store/purchasegroup[/…]` | `PurchaseGroup` |
| **Store BUY** | `PUT …/store/transaction` **and** `PUT …/store/transaction/<txn-id>` (numeric, e.g. `…/store/transaction/0`) | `StoreBuy` |
| Pack open | `POST …/purchased` | `PackOpen` |
| Pack reveal | `GET …/purchased` | `PackReveal` |
| Quick-sell (path) | `DELETE …/item/<digits>` | `QuickSellPath` |
| Quick-sell (body) | `POST (/ut/delete/game\|/ut/v2/delete/game)/<sku>/item` | `QuickSellBody` |
| Move | `PUT …/item` | `MoveItems` |
| Match end | `POST (/ut/delete/game\|/ut/v2/delete/game)/<sku>/match` | `MatchEnd` |
| Market list | `POST …/auctionhouse` \| `…/transfermarket` | `MarketList` |
| Market query | `GET …/tradePile` | `MarketQuery` |
| Market buy | `…/trade/<id>` | `MarketBuy` |
| Market cancel | `DELETE (/ut/delete/game\|/ut/v2/delete/game)/<sku>/trade/<id>` | `MarketCancel` |
`store/transaction` matching is BOUNDED to a single all-digit id segment — it
never absorbs `store/transactions`, `store/transactionfoo`, or
`store/transaction/<id>/extra` (those proxy to Python as non-economy). Audit
basis: Python route table `utas_server.py:1420` matches the store family
"regardless of /ut/game vs /ut/v2/game prefix"; all other economy routes are
`G = /ut/game/[^/]+`-prefixed (v1-only) and the retail client uses v1 for them.
## E1 — CUTOVER READY (barrier `93a46d4`, superproject source-ready; NOT deployed) ## E1 — CUTOVER READY (barrier `93a46d4`, superproject source-ready; NOT deployed)
The economy authority barrier is committed: `Server::handle_with_ip` dispatches The economy authority barrier is committed: `Server::handle_with_ip` dispatches