From 47ced228ded971b7cbe1650b93e770db997171e1 Mon Sep 17 00:00:00 2001 From: OpenFUT Agent Date: Thu, 13 Aug 2026 23:53:44 +0000 Subject: [PATCH] docs(route-authority): record v1/v2 economy URL prefix contract Accepted prefixes /ut/game// and /ut/v2/game// for every economy route; StoreBuy accepts store/transaction and store/transaction/. --- openfut-utas-host/ROUTE_AUTHORITY.md | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/openfut-utas-host/ROUTE_AUTHORITY.md b/openfut-utas-host/ROUTE_AUTHORITY.md index 5207888..1c06013 100644 --- a/openfut-utas-host/ROUTE_AUTHORITY.md +++ b/openfut-utas-host/ROUTE_AUTHORITY.md @@ -12,6 +12,38 @@ entitlements (`unopenedPackIds`). `points` has **no** writer (read-only). EASFC Legend: **R** = Rust/Core authoritative, **P** = Python proxied (oracle). 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//` +prefix (live-observed `PUT /ut/v2/game/fifa17/store/transaction/0`), while other +routes use `/ut/game//`. `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)//user/credits` | `Credits` | +| Store catalogue | `GET …/store/purchasegroup[/…]` | `PurchaseGroup` | +| **Store BUY** | `PUT …/store/transaction` **and** `PUT …/store/transaction/` (numeric, e.g. `…/store/transaction/0`) | `StoreBuy` | +| Pack open | `POST …/purchased` | `PackOpen` | +| Pack reveal | `GET …/purchased` | `PackReveal` | +| Quick-sell (path) | `DELETE …/item/` | `QuickSellPath` | +| Quick-sell (body) | `POST (/ut/delete/game\|/ut/v2/delete/game)//item` | `QuickSellBody` | +| Move | `PUT …/item` | `MoveItems` | +| Match end | `POST (/ut/delete/game\|/ut/v2/delete/game)//match` | `MatchEnd` | +| Market list | `POST …/auctionhouse` \| `…/transfermarket` | `MarketList` | +| Market query | `GET …/tradePile` | `MarketQuery` | +| Market buy | `…/trade/` | `MarketBuy` | +| Market cancel | `DELETE (/ut/delete/game\|/ut/v2/delete/game)//trade/` | `MarketCancel` | + +`store/transaction` matching is BOUNDED to a single all-digit id segment — it +never absorbs `store/transactions`, `store/transactionfoo`, or +`store/transaction//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) The economy authority barrier is committed: `Server::handle_with_ip` dispatches