Trade & store logistics: SPA /trade route (product surface for the whole set) #54

Closed
opened 2026-07-05 01:22:36 +00:00 by coilysiren · 2 comments
Owner

Context

The product surface for trade & store logistics under the DiscordLink-replacement epic (#37). AGENTS.md is emphatic: product UX is the SPA (frontend/), never a server-rendered card. This route is where the whole trade push becomes something a player actually opens, and it is the concrete embodiment of Kai's thesis that read-only trade info belongs on the website, not Discord.

No-reset spine

Pure frontend plus the /preview/*.json data plane the sibling backend issues expose. No server interaction beyond those endpoints, no reset. Follows the existing wave-0 SPA pattern (/economy, /crafting, /climate) - react-router page, live badges, filter-on-click, ?q= deep links, graceful degraded state, vitest component tests in CI.

Scope

The agent builds the SPA /trade route (frontend/src/pages/Trade.tsx or a small route cluster):

  • Market overview - movers (rising/falling items by trend), total volume, most-traded items. Consumes /preview/market.json.
  • Item drill-down - price history chart, recent trades, who is selling and buying. ?q=<item> deep link, cross-linked with /economy and /crafting.
  • Store & trader directory - browse stores and traders with volume, price points, owner, recency. Consumes /preview/stores.json.
  • Logistics board - cheapest-source, arbitrage spreads, and supply gaps. Consumes /preview/logistics.json. This is the headline "what should I do" panel.
  • Watchers panel - the caller's trade watchers and their recent hits. Consumes the watcher surface.
  • Currency strip - money-supply summary from /preview/currency.json.

Read the dataviz skill before building the price-history chart and any stat tiles, and register the new route on the homepage directory with a live badge.

This is the surface that makes the whole set exceed DiscordLink: one coherent, always-on web page with history, charts, logistics reasoning, and cross-links, versus DiscordLink's ephemeral DM embeds.

Acceptance

  • Route renders against live /preview/*.json and degrades gracefully when an endpoint is empty or 404 (some siblings may not have landed yet - the page must not hard-crash).
  • Registered on the homepage directory with a live badge, cross-linked with /economy and /crafting.
  • vitest component tests, ward exec frontend-test and ward exec frontend-lint green.
  • docs/FEATURES.md updated for the /trade route.

Dependencies

Consumes the data planes from the market, stores, logistics, watcher, and currency siblings. Can scaffold ahead of them against the documented /preview/*.json contracts and fill panels as they land. Parent: #37. Design pass: #2.

## Context The **product surface** for trade & store logistics under the DiscordLink-replacement epic ([#37](https://forgejo.coilysiren.me/coilyco-gaming/eco-app/issues/37)). AGENTS.md is emphatic: product UX is the SPA (`frontend/`), never a server-rendered card. This route is where the whole trade push becomes something a player actually opens, and it is the concrete embodiment of Kai's thesis that read-only trade info belongs on the website, not Discord. ## No-reset spine Pure frontend plus the `/preview/*.json` data plane the sibling backend issues expose. No server interaction beyond those endpoints, no reset. Follows the existing wave-0 SPA pattern (`/economy`, `/crafting`, `/climate`) - react-router page, live badges, filter-on-click, `?q=` deep links, graceful degraded state, vitest component tests in CI. ## Scope The agent builds the SPA `/trade` route (`frontend/src/pages/Trade.tsx` or a small route cluster): * **Market overview** - movers (rising/falling items by trend), total volume, most-traded items. Consumes `/preview/market.json`. * **Item drill-down** - price history chart, recent trades, who is selling and buying. `?q=<item>` deep link, cross-linked with `/economy` and `/crafting`. * **Store & trader directory** - browse stores and traders with volume, price points, owner, recency. Consumes `/preview/stores.json`. * **Logistics board** - cheapest-source, arbitrage spreads, and supply gaps. Consumes `/preview/logistics.json`. This is the headline "what should I do" panel. * **Watchers panel** - the caller's trade watchers and their recent hits. Consumes the watcher surface. * **Currency strip** - money-supply summary from `/preview/currency.json`. Read the `dataviz` skill before building the price-history chart and any stat tiles, and register the new route on the homepage directory with a live badge. ## DiscordLink parity This is the surface that makes the whole set **exceed** DiscordLink: one coherent, always-on web page with history, charts, logistics reasoning, and cross-links, versus DiscordLink's ephemeral DM embeds. ## Acceptance * Route renders against live `/preview/*.json` and degrades gracefully when an endpoint is empty or 404 (some siblings may not have landed yet - the page must not hard-crash). * Registered on the homepage directory with a live badge, cross-linked with `/economy` and `/crafting`. * vitest component tests, `ward exec frontend-test` and `ward exec frontend-lint` green. * `docs/FEATURES.md` updated for the `/trade` route. ## Dependencies Consumes the data planes from the market, stores, logistics, watcher, and currency siblings. Can scaffold ahead of them against the documented `/preview/*.json` contracts and fill panels as they land. Parent: [#37](https://forgejo.coilysiren.me/coilyco-gaming/eco-app/issues/37). Design pass: [#2](https://forgejo.coilysiren.me/coilyco-gaming/eco-app/issues/2).
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-eco-app-54 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-05T04:03:25Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-eco-app-54` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-05T04:03:25Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - SPA /trade product surface landed on main (currency strip, market movers, item drill-down, store/trader directory, logistics board, watchers panel), all vitest + lint + build green.

Honest retrospective: this one went smoothly, mostly because the ground was already well-prepared. The backend /preview/{market,stores,currency,watchers}.json planes were all live and the sibling /trades ledger page was a near-perfect template for the chart, rank rows, and cross-link conventions, so a lot of this was assembling existing pieces into one coherent page rather than inventing.

The one thing that fought back a little was the naming collision: /trades (the existing row-level ledger) versus /trade (this new product surface). It took a beat to be sure I was building a distinct route, not editing the ledger. I leaned into it by cross-linking them explicitly and giving each a clear homepage card.

The real design decision was graceful degradation. /preview/logistics.json has no backend yet (no logistics module exists), so I froze a documented contract in logisticsApi.ts, made every one of the five planes fetch independently via a best-effort fetchJsonOrNull, and had each panel hide itself on a 404 rather than take the page down. A test covers the all-planes-404 case and the logistics-only-404 case, so I am fairly confident it will not hard-crash as siblings land or lag.

Confidence is high on the frontend: 46 tests pass (7 new), lint and the production tsc build are clean, and every panel plus both degrade paths are exercised. The rough edge worth naming: I could not drive it against the live game server from here, so the panels are verified against fixtures, not real payloads. Two follow-ups worth filing when the backend catches up: (1) a /preview/logistics.json backend to fill the logistics board, and (2) the item drill-down's "recent trades / who is buying and selling" is currently a cross-link to /trades?q=, which could be inlined once a per-item trade slice is cheap to fetch.

WARD-OUTCOME: done - SPA /trade product surface landed on main (currency strip, market movers, item drill-down, store/trader directory, logistics board, watchers panel), all vitest + lint + build green. Honest retrospective: this one went smoothly, mostly because the ground was already well-prepared. The backend `/preview/{market,stores,currency,watchers}.json` planes were all live and the sibling `/trades` ledger page was a near-perfect template for the chart, rank rows, and cross-link conventions, so a lot of this was assembling existing pieces into one coherent page rather than inventing. The one thing that fought back a little was the naming collision: `/trades` (the existing row-level ledger) versus `/trade` (this new product surface). It took a beat to be sure I was building a distinct route, not editing the ledger. I leaned into it by cross-linking them explicitly and giving each a clear homepage card. The real design decision was graceful degradation. `/preview/logistics.json` has no backend yet (no logistics module exists), so I froze a documented contract in `logisticsApi.ts`, made every one of the five planes fetch independently via a best-effort `fetchJsonOrNull`, and had each panel hide itself on a 404 rather than take the page down. A test covers the all-planes-404 case and the logistics-only-404 case, so I am fairly confident it will not hard-crash as siblings land or lag. Confidence is high on the frontend: 46 tests pass (7 new), lint and the production tsc build are clean, and every panel plus both degrade paths are exercised. The rough edge worth naming: I could not drive it against the live game server from here, so the panels are verified against fixtures, not real payloads. Two follow-ups worth filing when the backend catches up: (1) a `/preview/logistics.json` backend to fill the logistics board, and (2) the item drill-down's "recent trades / who is buying and selling" is currently a cross-link to `/trades?q=`, which could be inlined once a per-item trade slice is cheap to fetch.
Sign in to join this conversation.
No description provided.