The app currently has nine nav items and sixteen routes, each one a table of a noun. This proposes five places and one action layer. The reason it can collapse that far is not taste — it is that the API cannot write at all, so every change settles through one of three paths, and two of those are shapes a conventional form cannot express.
Most IA proposals argue from preference. This one starts from the write surface that already exists — the GraphQL API, the pilot's tool definitions, and the eight deployed contracts. What the product can and cannot save is a fact, and it constrains the design more than any opinion does.
src/graphql/builder.ts:63 has no
builder.mutationType — zero mutations. Nothing a player does is ever saved by the
API, so every write settles one of the three ways below and no screen can assume otherwise.Two of the three are unusual enough to shape the whole interface
update_race_orders validates, clamps and persists server-side — strategy,
aggression, risk, line, overtaking, corner risk, boost discipline, exit boost, boost braking.
There is no endpoint that edits orders directly, so this is the one function in the
product that is unreachable except by conversation.equip, repair*, recycle*, buyPack,
buyAndOpen, enterRace, clone, setCloneFee,
claimPrize, buyCredits. A form can do this perfectly well —
LoadoutStudio.tsx already does, with a Save on-chain button that commits all five
slots in one atomic LoadoutVault.equip.Fabricator.fabricate is
player-called and payable, but it verifies an orchestrator EIP-712 signature over the
exact (player, parentA, parentB, child, materialTier, materialAmount, nonce, deadline).
The server decides what the child will be and signs it; the player pays and submits. A form
alone cannot start this — the authorisation has to be issued first.src/services/llm/pilotChat.ts:634 — if (balance < cost) return { ok: false,
error: "insufficient_credits" }. At zero compute the pilot cannot answer, so chat can
never be the only route to anything essential — and above all not to the thing that restores
it. A chat-only storefront cannot sell you compute: you would need compute to ask for it. The
rule this forces: compute buys advice, never access. Spending money, collecting money and
restoring compute all need a route that costs nothing to reach — which is settled by selling
compute in the Shop, as its own SKUs and bundled into packs. The storefront is then the
recovery route, and it is the one place that must work at a zero balance.
Path A is why a settings panel for orders cannot work: there is nothing to submit it to. Path C is why fabrication cannot be a pure client form: the outcome is authorised off-chain before it can be paid for.
Path B, though, is ordinary — and that is the correction this document needed. Chat is not the only way to write, and it should not be. It earns its place where a decision wants advice, where the server must co-sign, or where orders are involved. Where a player already knows exactly which two pieces to swap, direct manipulation is better, and the existing loadout editor is evidence that it works.
One settlement path per action, reachable more than one way.
loadout proposal and the same single equip call — never two code
paths that can disagree about what is equipped.Read this top to bottom as one causal chain: an open surface anyone can see without a wallet, five places you can stand, a chat layer that lives inside every place rather than beside them, what that chat can emit, and where it settles.
Chat is drawn as a bar and not as a box because it is not a sixth destination — it is the verb layer, present wherever a pilot is.
Nothing is deleted — every capability survives. What changes is that a capability stops being a place you navigate to and becomes a thing you ask for.
The test applied to each row: does standing here, looking at a list, have value on its own? If the only reason to visit is to perform one action, it is a proposal, not a route.
| Route today | Nav label | Where it goes, and why | Verdict |
|---|---|---|---|
| / | Home | Becomes On Air. A dashboard of links has no value when the live race is the product's best advertisement for itself. | becomes On Air |
| /races /races/:raceId | Race Lobby | The Card. Genuinely a place — you compare fields, venues and eligibility before committing money. | keep |
| /garage /garage/:pilotId | Hangar | Pilots. Also genuinely a place — the dossier is reference material you read. | keep, rename |
| /garage/:pilotId/loadout | — | Keep it, and back it with chat. It works
— Save on-chain commits all five slots in one atomic LoadoutVault.equip. Direct
manipulation is the better tool when you know which two pieces to swap; chat is better for "which
of these is actually an upgrade". Both should emit the same loadout proposal so there
is one settlement path. | keep + chat |
| /gear/:tokenId | — | Stays as a deep-linkable page — a gear piece is a tradeable asset and needs a URL — but is reached as a dialog from a pilot. | keep as detail |
| /inventory | Inventory | Keep — it is wallet-scoped, not pilot-scoped.
Unequipped gear, unopened packs, scrap, entry credits and compute all belong to the
wallet (useScrapBalances, useEntryCredits and
useComputeCredits all key on the account address). There is frequently no pilot to
fold them into, so folding would leave them homeless. | keep |
| /shop | Shop | Keep — this one is load-bearing. Chat is gated on compute,
so a chat-only shop cannot sell you compute; you would need compute to ask. It is also where
revenue lives, and revenue must never depend on a metered feature. It sells compute directly
— as its own SKUs and bundled into packs — so the storefront is also the recovery route. The
pilot still advises which pack via a pack proposal, but the Shop stands
alone and works at a zero balance. BuyCreditsModal.tsx is already this,
narrowly.Build cost: SKU tiers are free — CreditTopUp takes any amount and the backend converts at a configured rate, so
pricing changes without a redeploy. Bundles need a backend rule, not a contract change:
PackType has no compute field. | keep |
| /cloning /studs → /cloning | Cloning | Bloodline. The lineage
itself is worth looking at; the two actions on it (clone,
stud_listing) become proposals. | keep, rename |
| /mint | Mint | Not a nav item — a CTA on On Air. Nobody navigates to "Mint"; they see a race and want in. It stays a URL for campaign links. | fold to CTA |
| /history /leaderboards | History Leaderboards | Merge into Records. Both answer "what has happened and where do I stand" — splitting them means neither answers it fully. | merge |
| /studio | — | An asset/render tool. Real value, but for us, not players. Keep it out of the player IA and un-navved. | internal |
| /admin | — | Operator surface. | internal |
Ranked by consequence of failure, not frequency. Flow 1 is first because nothing else can happen until it works; flow 2 is second because it is the only one players repeat, so it is the only one whose quality compounds.
Each step carries the layer it happens in and what it costs — a step that costs a signature is a step a player can refuse, and every one of those is a place the flow can die. Each flow ends in an explicit "dies if".
update_race_orders needs no wallet, so there is
nothing to approve and nothing to lose.claimRefund; being cloned as a stud accrues an 80% owner share that needs withdrawCloneEarnings.gas · 1 signature eachsetCloneFee against the cloneMinFee floor. Income then accrues and must be withdrawn.gas · 1 signatureCreditsPurchased(buyer, weiPaid) — an on-chain
receipt the indexer mirrors. Compute bundled into a pack has no such event, because
PackType carries no compute field, so it would arrive by a backend rule keyed on
the pack purchase. That is the one number on a receipt that would not trace to a signed
source, which cuts against the product's own claim.PackType a compute field so the
grant is provable rather than asserted.