# Architect — F-015 SEO core: structured data, sitemap, redirects

## Feature
F-015 adds SEO crawlability and structured data to the existing F-014 storefront catalog pages.

## Design

### Storefront SEO module
Create a small storefront SEO module under `project/storefront/src/lib/seo/`:
- `json-ld.ts` builds typed JSON-LD objects for Organization, BreadcrumbList and Product.
- `redirects.ts` defines a `RedirectRepository` interface and an env-backed implementation.
- Keep public URL generation centralized through existing `absoluteUrl()` / `SITE_URL`.

No new runtime dependency is required.

### Structured data
- Root layout embeds Organization JSON-LD once.
- Product pages embed:
  - Product JSON-LD using product name, description, canonical URL and main image when available.
  - BreadcrumbList JSON-LD for Home → Product.
- Category and brand pages embed BreadcrumbList JSON-LD.
- JSON-LD is rendered as `application/ld+json` with JSON serialization, not string concatenation.

### Sitemap and robots
Use Next.js metadata routes:
- `src/app/sitemap.ts` returns `MetadataRoute.Sitemap`.
- `src/app/robots.ts` returns `MetadataRoute.Robots`.

Sitemap source:
- Static public URLs: `/`, `/products/search`.
- Product URLs from `searchProducts({ limit: 100, offset })`, which returns active products only through backend `SearchProducts.activeOnly = true`; page through results to avoid silently truncating the first page.
- Category URLs from `listCategoryTree()` flattened recursively.
- Brand URLs from a new public `GET /brands` backend endpoint and storefront `listBrands()` client helper.
- On API failure, sitemap degrades to static URLs rather than failing the build.

### Redirect management
- Add `src/middleware.ts` using an env-backed redirect store.
- `RedirectRepository.findBySourcePath(pathname)` returns a redirect target when configured.
- Configure redirects with `REDIRECTS_JSON`, an array of `{ "from": "/old", "to": "/new" }` entries.
- Middleware returns permanent `301` redirects only for same-origin local paths. External targets are rejected by the parser to avoid open redirects.
- Redirect source and target paths must start with `/`; source cannot equal target.

### Backend brand listing
Extend the brands module minimally:
- Add `BrandRepository.list()`.
- Add `ListBrands` use case.
- Add public `GET /brands` returning `{ items: [...] }` in stable `name ASC, id ASC` order.

## Acceptance trace
- Product JSON-LD: product page embeds Product schema script.
- Sitemap: lists active product URLs via search API, category URLs, brand URLs and static pages; draft products excluded by backend active search.
- Redirect: middleware redirects stored old path with HTTP 301 to same-origin local target.
- Canonical URLs: existing F-014 canonical metadata remains on product/category/brand/search and root layout gains canonical metadata.
- `verify.sh`: must pass after gates.

## Risks / constraints
- No hreflang and no external SEO tooling by scope.
- Lighthouse/Google validator may be unavailable locally; validate structurally and through build output.
- Env redirects are enough for this slice; a database-backed admin UI is intentionally deferred.
