Recipes browse page (/recipes + /recipe?id=) - follow-up B from #98 [depends on A] #101
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The recipes browse surface from the #98 design: a recipe list + detail analogous to
/items->/item, reading the recipe data that follow-up A hydrates. This is follow-up B.Depends on #98-A (the recipe data ingest /
/preview/recipes.json). Do not start until A has landed a real recipe payload.Scope
/recipes- list page modeled onpages/Items.tsx: URL-driven?q=filter (client-side, on product + ingredient pretty names, capped likeLIST_ROWS), rows deep-link to/recipe?id=.... Numbers vialib/format.ts./recipe?id=...- detail modeled onpages/Item.tsx(query-param key + thefetched.id === idstale-guard). Shows the BOM tree, station, required skill + level, labor / calorie / time cost, and cross-links: the product's current market price (market.py), who has crafted it (crafting.pyby_crafted), and where to buy / sell it (logistics.py).skill.name), by station, by ingredient (reverse lookup - "what can I make with X", the highest-value filter), by product (default search), by table tier.lib/recipesApi.tstyped client mirroring/preview/recipes.json./recipeis URL-only (reached from/recipes), one hub card inHome.tsx, nav link inLayout.tsxonly if it earns primary nav.Conventions & done
One heading tier + minimal prose,
Layout/Hero, vitest component tests including the empty/degraded branch. Updatedocs/FEATURES.md. Green onward exectest + lint. Full design context on #98.🔒 Reserved by
ward agent --driver claude— containerengineer-claude-eco-app-101on hostKAI-DESKTOP-TOWERis carrying this issue (reserved 2026-07-07T07:16:44Z). Concurrentward agentruns are blocked until it finishes or the reservation goes stale (2h0m0s TTL);--forceoverrides.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).
run seed context — what this run is carrying (ward#609)
coilyco-gaming/eco-app#101· branchissue-101· driverclaude· workflowdirect-mainengineer-claude-eco-app-101· wardv0.419.0· dispatched2026-07-07T07:16:44ZIssue body as seeded:
Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.419.0).
— Claude (she/her), via
ward agentWARD-OUTCOME: done - /recipes directory + /recipe?id= detail shipped over the static recipe graph, with skill/station/ingredient/product filters, market cross-links, nav + Home card, and vitest coverage; landed on main (
f82dc51).Honest retrospective: this one went smoothly, mostly because follow-up A did the hard part. The recipe DTO from
recipes.pywas already clean and complete, so the frontend was largely a faithful copy of the/items->/itempattern with the fields swapped. Modeling on the existing pages paid off — the URL-driven filter/update helpers ported almost verbatim.Two things fought back mildly. First, the one place I consciously diverged from the seed: it asks for the
fetched.id === idstale-guard fromItem.tsx, but the recipe graph is a single static bundled payload with no per-id endpoint, so there is genuinely nothing to guard — I fetch the index once and resolve the recipe client-side by name. I kept the empty/not-found/error branches the guard exists to protect and left a comment explaining the divergence, rather than mechanically copying a guard that would never fire. Second,?tier=(table tier) is dead in the vanilla seed —tableTierRequiredis null everywhere until the cost engine (C) derives it — so I made the tier facet self-hiding until real values land, instead of shipping an empty control.Confidence is high: 14 new component tests cover the filters, the reverse-lookup ('what can I make with X'), and every empty/degraded/not-found branch; frontend-test/build/lint and the precommit suite are all green; and I confirmed the live server serves
/preview/recipes.json(1,453 recipes) and both SPA routes 200. I couldn't drive the rendered page through the Playwright MCP — its browser sandbox can't reach the container's localhost — so runtime verification leaned on curl + the component suite rather than a real click-through.Rough edges / possible follow-ups: the full
recipes.jsonis ~1.3MB uncompressed (well-cached, gzips to ~150KB, shared by both routes, so fine for now, but a lighter list projection would help if it grows); and the market cross-link is a link to/item, not embedded live price data on the recipe page itself — a deliberate scope call reading 'cross-links' literally, worth revisiting once the cost engine (C) gives the recipe page its own pricing to show.