### Page
- Page URL: http://shop.test/search?q=cotton
- Page Title: Search - Acme Fashion
### Snapshot
```yaml
- generic [active] [ref=e1]:
  - link "Skip to main content" [ref=e2] [cursor=pointer]:
    - /url: "#main-content"
  - generic [ref=e4]:
    - paragraph [ref=e5]: Free shipping on orders over 50.00 USD
    - button "Dismiss announcement" [ref=e6]:
      - img [ref=e7]
  - banner [ref=e9]:
    - generic [ref=e10]:
      - link "Acme Fashion" [ref=e11] [cursor=pointer]:
        - /url: http://shop.test
        - generic [ref=e12]: Acme Fashion
      - navigation "Main" [ref=e13]:
        - link "Home" [ref=e14] [cursor=pointer]:
          - /url: /
        - button "Shop" [ref=e16]:
          - text: Shop
          - img [ref=e17]
        - link "About" [ref=e19] [cursor=pointer]:
          - /url: /pages/about
        - link "Contact" [ref=e20] [cursor=pointer]:
          - /url: /pages/contact
      - generic [ref=e21]:
        - button "Search" [ref=e22]:
          - img [ref=e23]
        - link "Log in" [ref=e25] [cursor=pointer]:
          - /url: http://shop.test/account/login
          - img [ref=e26]
        - button "Cart" [ref=e28]:
          - img [ref=e29]
  - main [ref=e31]:
    - generic [ref=e32]:
      - navigation "Breadcrumb" [ref=e33]:
        - list [ref=e34]:
          - listitem [ref=e35]:
            - link "Home" [ref=e36] [cursor=pointer]:
              - /url: http://shop.test
            - generic [ref=e37]: /
          - listitem [ref=e38]:
            - generic [ref=e39]: Search results
      - heading "2 results for 'cotton'" [level=1] [ref=e40]
      - generic [ref=e41]:
        - searchbox "Search" [ref=e42]: cotton
        - button "Search" [ref=e43]
      - generic [ref=e44]:
        - generic [ref=e45]: Sort by
        - combobox "Sort by" [ref=e46]:
          - option "Relevance" [selected]
          - 'option "Price: Low to High"'
          - 'option "Price: High to Low"'
          - option "Newest"
          - option "Best Selling"
      - generic [ref=e47]:
        - generic [ref=e48]:
          - link "Organic Cotton Hoodie Sale On sale Organic Cotton Hoodie 65.00 USD 80.00 USD Sale" [ref=e49] [cursor=pointer]:
            - /url: /products/organic-cotton-hoodie
            - generic [ref=e50]:
              - img "Organic Cotton Hoodie" [ref=e51]
              - generic [ref=e52]:
                - generic [ref=e53]: Sale
                - generic [ref=e54]: On sale
            - generic [ref=e55]:
              - heading "Organic Cotton Hoodie" [level=3] [ref=e56]
              - generic [ref=e57]:
                - generic [ref=e58]: 65.00 USD
                - generic [ref=e59]: 80.00 USD
                - generic [ref=e60]: Sale
          - link "View product" [ref=e62] [cursor=pointer]:
            - /url: /products/organic-cotton-hoodie
        - generic [ref=e63]:
          - link "Classic Cotton T-Shirt Sale On sale Classic Cotton T-Shirt 25.00 USD 40.00 USD Sale" [ref=e64] [cursor=pointer]:
            - /url: /products/classic-cotton-t-shirt
            - generic [ref=e65]:
              - img "Classic Cotton T-Shirt" [ref=e66]
              - generic [ref=e67]:
                - generic [ref=e68]: Sale
                - generic [ref=e69]: On sale
            - generic [ref=e70]:
              - heading "Classic Cotton T-Shirt" [level=3] [ref=e71]
              - generic [ref=e72]:
                - generic [ref=e73]: 25.00 USD
                - generic [ref=e74]: 40.00 USD
                - generic [ref=e75]: Sale
          - link "View product" [ref=e77] [cursor=pointer]:
            - /url: /products/classic-cotton-t-shirt
  - contentinfo [ref=e78]:
    - generic [ref=e79]:
      - generic [ref=e80]:
        - generic [ref=e81]:
          - heading "Shop" [level=2] [ref=e82]
          - list [ref=e83]:
            - listitem [ref=e84]:
              - link "Collections" [ref=e85] [cursor=pointer]:
                - /url: /collections
        - generic [ref=e86]:
          - heading "Information" [level=2] [ref=e87]
          - list [ref=e88]:
            - listitem [ref=e89]:
              - link "About" [ref=e90] [cursor=pointer]:
                - /url: /pages/about
            - listitem [ref=e91]:
              - link "Contact" [ref=e92] [cursor=pointer]:
                - /url: /pages/contact
        - generic [ref=e93]:
          - heading "Acme Fashion" [level=2] [ref=e94]
          - paragraph [ref=e95]: Acme Fashion — quality goods, delivered.
      - generic [ref=e96]:
        - paragraph [ref=e97]: © 2026 Acme Fashion. All rights reserved.
        - generic [ref=e98]:
          - link "Facebook" [ref=e99] [cursor=pointer]:
            - /url: https://facebook.com
            - generic [ref=e100]: Facebook
            - img [ref=e101]
          - link "Instagram" [ref=e104] [cursor=pointer]:
            - /url: https://instagram.com
            - generic [ref=e105]: Instagram
            - img [ref=e106]
```


✅ **Search works** — FTS5 returns "2 results for 'cotton'" (Organic Cotton Hoodie + Classic Cotton T-Shirt) with sort options, images, sale badges.

Now the **admin panel**. Let me log in.


### Ran Playwright code
```js
await page.goto('http://shop.test/admin/login');
```
### Page
- Page URL: http://shop.test/
- Page Title: Acme Fashion
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-36-01-499Z.yml)
### Events
- New console entries: .playwright-mcp/console-2026-05-31T14-36-01-374Z.log#L1


Unexpected — `/admin/login` redirected to `/` (home). Let me investigate whether this is an auth-state issue or a routing bug. Let me check `/admin` directly.


### Ran Playwright code
```js
await page.goto('http://shop.test/admin');
```
### Page
- Page URL: http://shop.test/admin
- Page Title: Admin
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-36-18-787Z.yml)
### Events
- New console entries: .playwright-mcp/console-2026-05-31T14-36-18-590Z.log#L1


I'm already authenticated as admin (session persisted from a teammate's earlier browser verification) — that's why `/admin/login` redirected away. `/admin` loads directly (title "Admin"). Login itself was already verified by foundation/admin. Minor note: an authenticated admin hitting `/admin/login` redirects to `/` rather than `/admin` — low-priority polish. Let me verify the dashboard.


### Page
- Page URL: http://shop.test/admin
- Page Title: Admin
### Snapshot
```yaml
- generic [active] [ref=e1]:
  - generic [ref=e3]:
    - link "Acme Fashion" [ref=e5] [cursor=pointer]:
      - /url: http://shop.test/admin
      - img [ref=e8]
      - generic [ref=e10]: Acme Fashion
    - navigation [ref=e11]:
      - link "Dashboard" [ref=e13] [cursor=pointer]:
        - /url: http://shop.test/admin
        - img [ref=e15]
        - generic [ref=e17]: Dashboard
      - generic [ref=e18]:
        - generic [ref=e20]: Products
        - generic [ref=e21]:
          - link "Products" [ref=e23] [cursor=pointer]:
            - /url: http://shop.test/admin/products
            - img [ref=e25]
            - generic [ref=e27]: Products
          - link "Collections" [ref=e29] [cursor=pointer]:
            - /url: http://shop.test/admin/collections
            - img [ref=e31]
            - generic [ref=e33]: Collections
          - link "Inventory" [ref=e35] [cursor=pointer]:
            - /url: http://shop.test/admin/inventory
            - img [ref=e37]
            - generic [ref=e39]: Inventory
      - generic [ref=e40]:
        - generic [ref=e42]: Orders
        - link "Orders" [ref=e45] [cursor=pointer]:
          - /url: http://shop.test/admin/orders
          - img [ref=e47]
          - generic [ref=e49]: Orders
      - generic [ref=e50]:
        - generic [ref=e52]: Customers
        - link "Customers" [ref=e55] [cursor=pointer]:
          - /url: http://shop.test/admin/customers
          - img [ref=e57]
          - generic [ref=e59]: Customers
      - generic [ref=e60]:
        - generic [ref=e62]: Discounts
        - link "Discounts" [ref=e65] [cursor=pointer]:
          - /url: http://shop.test/admin/discounts
          - img [ref=e67]
          - generic [ref=e70]: Discounts
      - generic [ref=e71]:
        - generic [ref=e73]: Content
        - generic [ref=e74]:
          - link "Pages" [ref=e76] [cursor=pointer]:
            - /url: http://shop.test/admin/pages
            - img [ref=e78]
            - generic [ref=e80]: Pages
          - link "Navigation" [ref=e82] [cursor=pointer]:
            - /url: http://shop.test/admin/navigation
            - img [ref=e84]
            - generic [ref=e86]: Navigation
          - link "Themes" [ref=e88] [cursor=pointer]:
            - /url: http://shop.test/admin/themes
            - img [ref=e90]
            - generic [ref=e92]: Themes
    - navigation [ref=e93]:
      - link "Analytics" [ref=e95] [cursor=pointer]:
        - /url: http://shop.test/admin/analytics
        - img [ref=e97]
        - generic [ref=e100]: Analytics
      - link "Settings" [ref=e102] [cursor=pointer]:
        - /url: http://shop.test/admin/settings
        - img [ref=e104]
        - generic [ref=e107]: Settings
      - link "Apps" [ref=e109] [cursor=pointer]:
        - /url: http://shop.test/admin/apps
        - img [ref=e111]
        - generic [ref=e113]: Apps
      - link "Developers" [ref=e115] [cursor=pointer]:
        - /url: http://shop.test/admin/developers
        - img [ref=e117]
        - generic [ref=e119]: Developers
  - banner [ref=e121]:
    - paragraph [ref=e122]: Acme Fashion
    - button "Notifications" [ref=e123]:
      - img [ref=e124]
    - button "DO Demo Owner" [ref=e127]:
      - generic [ref=e130]: DO
      - generic [ref=e131]: Demo Owner
      - img [ref=e133]
  - generic [ref=e136]:
    - generic [ref=e137]:
      - generic [ref=e138]:
        - link [ref=e139] [cursor=pointer]:
          - /url: http://shop.test/admin
          - img [ref=e140]
        - img [ref=e142]
      - generic [ref=e145]: Dashboard
    - generic [ref=e146]:
      - heading "Dashboard" [level=1] [ref=e147]
      - combobox [ref=e149]:
        - option "Today"
        - option "Last 7 days"
        - option "Last 30 days" [selected]
        - option "Custom range"
    - generic [ref=e150]:
      - generic [ref=e151]:
        - paragraph [ref=e152]: Total Sales
        - generic [ref=e153]: 960.99 USD
        - generic [ref=e155]:
          - img [ref=e156]
          - text: +100%
      - generic [ref=e158]:
        - paragraph [ref=e159]: Orders
        - generic [ref=e160]: "8"
        - generic [ref=e162]:
          - img [ref=e163]
          - text: +100%
      - generic [ref=e165]:
        - paragraph [ref=e166]: Avg Order Value
        - generic [ref=e167]: 120.12 USD
        - generic [ref=e169]:
          - img [ref=e170]
          - text: +100%
      - generic [ref=e172]:
        - paragraph [ref=e173]: Conversion Rate
        - generic [ref=e174]: 0.0%
        - generic [ref=e176]: Coming soon
    - generic [ref=e177]:
      - generic [ref=e178]: Orders over time
      - img "Daily order counts for the last 30 days" [ref=e179]
    - generic [ref=e180]:
      - generic [ref=e181]: Recent orders
      - table [ref=e184]:
        - rowgroup [ref=e185]:
          - row "Order Date Customer Status Total" [ref=e186]:
            - columnheader "Order" [ref=e187]:
              - generic [ref=e188]: Order
            - columnheader "Date" [ref=e189]:
              - generic [ref=e190]: Date
            - columnheader "Customer" [ref=e191]:
              - generic [ref=e192]: Customer
            - columnheader "Status" [ref=e193]:
              - generic [ref=e194]: Status
            - columnheader "Total" [ref=e195]:
              - generic [ref=e196]: Total
        - rowgroup [ref=e197]:
          - row "#1009 May 31, 2026 2:35 PM Guest Paid 41.02 USD" [ref=e198]:
            - cell "#1009" [ref=e199]:
              - link "#1009" [ref=e200] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/9
            - cell "May 31, 2026 2:35 PM" [ref=e201]
            - cell "Guest" [ref=e202]
            - cell "Paid" [ref=e203]:
              - generic [ref=e204]: Paid
            - cell "41.02 USD" [ref=e205]
          - row "#1003 May 27, 2026 2:33 PM Liam Müller Pending 133.43 USD" [ref=e206]:
            - cell "#1003" [ref=e207]:
              - link "#1003" [ref=e208] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/3
            - cell "May 27, 2026 2:33 PM" [ref=e209]
            - cell "Liam Müller" [ref=e210]
            - cell "Pending" [ref=e211]:
              - generic [ref=e212]: Pending
            - cell "133.43 USD" [ref=e213]
          - row "#1002 May 27, 2026 2:33 PM Jane Smith Pending 94.55 USD" [ref=e214]:
            - cell "#1002" [ref=e215]:
              - link "#1002" [ref=e216] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/2
            - cell "May 27, 2026 2:33 PM" [ref=e217]
            - cell "Jane Smith" [ref=e218]
            - cell "Pending" [ref=e219]:
              - generic [ref=e220]: Pending
            - cell "94.55 USD" [ref=e221]
          - row "#1005 May 26, 2026 2:33 PM Demo Customer Paid 151.79 USD" [ref=e222]:
            - cell "#1005" [ref=e223]:
              - link "#1005" [ref=e224] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/5
            - cell "May 26, 2026 2:33 PM" [ref=e225]
            - cell "Demo Customer" [ref=e226]
            - cell "Paid" [ref=e227]:
              - generic [ref=e228]: Paid
            - cell "151.79 USD" [ref=e229]
          - row "#1004 May 25, 2026 2:33 PM Aisha Khan Paid 210.11 USD" [ref=e230]:
            - cell "#1004" [ref=e231]:
              - link "#1004" [ref=e232] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/4
            - cell "May 25, 2026 2:33 PM" [ref=e233]
            - cell "Aisha Khan" [ref=e234]
            - cell "Paid" [ref=e235]:
              - generic [ref=e236]: Paid
            - cell "210.11 USD" [ref=e237]
          - row "#1007 May 23, 2026 2:33 PM Liam Müller Partially refunded 61.07 USD" [ref=e238]:
            - cell "#1007" [ref=e239]:
              - link "#1007" [ref=e240] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/7
            - cell "May 23, 2026 2:33 PM" [ref=e241]
            - cell "Liam Müller" [ref=e242]
            - cell "Partially refunded" [ref=e243]:
              - generic [ref=e244]: Partially refunded
            - cell "61.07 USD" [ref=e245]
          - row "#1006 May 23, 2026 2:33 PM Jane Smith Voided 160.43 USD" [ref=e246]:
            - cell "#1006" [ref=e247]:
              - link "#1006" [ref=e248] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/6
            - cell "May 23, 2026 2:33 PM" [ref=e249]
            - cell "Jane Smith" [ref=e250]
            - cell "Voided" [ref=e251]:
              - generic [ref=e252]: Voided
            - cell "160.43 USD" [ref=e253]
          - row "#1001 May 23, 2026 2:33 PM Demo Customer Paid 115.07 USD" [ref=e254]:
            - cell "#1001" [ref=e255]:
              - link "#1001" [ref=e256] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/1
            - cell "May 23, 2026 2:33 PM" [ref=e257]
            - cell "Demo Customer" [ref=e258]
            - cell "Paid" [ref=e259]:
              - generic [ref=e260]: Paid
            - cell "115.07 USD" [ref=e261]
          - row "#1008 May 20, 2026 2:33 PM Aisha Khan Refunded 153.95 USD" [ref=e262]:
            - cell "#1008" [ref=e263]:
              - link "#1008" [ref=e264] [cursor=pointer]:
                - /url: http://shop.test/admin/orders/8
            - cell "May 20, 2026 2:33 PM" [ref=e265]
            - cell "Aisha Khan" [ref=e266]
            - cell "Refunded" [ref=e267]:
              - generic [ref=e268]: Refunded
            - cell "153.95 USD" [ref=e269]
```


Your mission is to implement an entire shop system based on the specifications im specs/*. You must do in one go without stopping. You must use team mode! You must test everything via Pest (unit, and…


Implement complete shop system with testing and review


The admin dashboard is excellent — full sidebar nav, KPI tiles (**Total Sales 960.99 USD, 8 orders, AOV 120.12 USD**, Conversion "Coming soon"), orders chart, and the recent-orders table showing all 8 orders across statuses **including my just-placed #1009 (Paid, 41.02 USD)** — confirming the storefront order flowed into admin. Let me identify the bank-transfer order to test Confirm Payment, then open order details.


Question: SELECT id, order_number, payment_method, status, financial_status, fulfillment_status FROM orders ORDER BY id;


Order **#1003 (id 3) is the bank_transfer + pending** order — ideal for testing Confirm Payment (which dispatches `OrderPaid` → webhook, now handled gracefully). Let me open it.


### Ran Playwright code
```js
await page.goto('http://shop.test/admin/orders/3');
```
### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-37-01-274Z.yml)
### Events
- New console entries: .playwright-mcp/console-2026-05-31T14-37-01-105Z.log#L1


Phase 1: Foundation (config, tenancy, models, auth, policies)

CRITICAL — blocks everything. Read specs/01-DATABASE-SCHEMA.md, specs/06-AUTH-AND-SECURITY.md, and specs/09-IMPLEMENTATION-ROADMAP.md Phase 1.

Deliver:
1. Config: add `customer` guard + `customers` provider + `customers` password broker to config/auth.php (Customer model). SQLite tuning in config/database.php (WAL, foreign_keys, busy_timeout=5000, synchronous=normal). JSON structured logging channel in config/logging.php.
2. Migrations (dependency order): create_organizations_table, create_stores_table (FK organization_id), create_store_domains_table (FK store_id), modify users table (add status, last_login_at; keep two_factor cols), create_store_users_table (composite store_id+user_id, role), create_store_settings_table (PK store_id). Money = INTEGER minor units. Enums = TEXT with CHECK. Timestamps ISO-8601.
3. Models + factories + seeders: Organization, Store, StoreDomain, StoreUser (custom Pivot w/ role), StoreSettings. Relationships per roadmap. Explicit return types, casts() method, $fillable.
4. Enums in app/Enums/: StoreStatus(Active,Suspended), StoreUserRole(Owner,Admin,Staff,Support), StoreDomainType(Storefront,Admin,Api). TitleCase keys.
5. Middleware App\Http\Middleware\ResolveStore: storefront resolves Store by Host header from store_domains (cache hostname->store_id 5min, bind singleton container 'current_store'); admin reads session current_store_id + verifies store_users; 404 unknown host; 503 suspended. Register `storefront` and `admin` middleware groups in bootstrap/app.php.
6. Trait App\Models\Concerns\BelongsToStore + Scope App\Models\Scopes\StoreScope (where store_id = app('current_store')->id; auto-set store_id on creating).
7. Admin auth: Livewire Admin\Auth\Login + Logout, Auth::guard('web')->attempt, rate limit 5/min/IP (RateLimiter::for('login') in AppServiceProvider), password reset via Password::broker('users'), record last_login_at, regenerate session.
8. Customer auth: customer guard, CustomerUserProvider injecting store_id, email unique per store. (Customer model lives in Phase 5 — coordinate: create a minimal Customer model + migration here OR leave provider ready; create customers table migration here so guard works. Add customers + customer_addresses migrations + Customer model here to unblock auth, Phase 5 extends.)
9. Policies for Product, Order, Collection, Discount, Customer, Store, Page, Theme, Fulfillment, Refund using User::roleForStore(Store): ?StoreUserRole + permission matrix in roadmap. Register via AppServiceProvider.
10. Pest helpers in tests/Pest.php (or a trait): createStoreContext() (Organization+Store+StoreDomain+Owner User, binds current_store), actingAsAdmin(User), actingAsCustomer(Customer).
11. Route skeleton: routes/storefront.php, routes/admin.php, routes/api.php (empty stubs with middleware groups), wire into bootstrap/app.php. Replace welcome route. Remove/disable starter-kit tests that no longer apply (DashboardTest, Settings/* if they conflict) — but keep auth working.

Write Pest tests for: TenantResolution, StoreIsolation, AdminAuth, CustomerAuth. Run them green. Run `vendor/bin/pint --dirty --format agent`. Report conventions (base model pattern, money casting, how to use createStoreContext) back to team-lead so other domains follow them.

Phase 2: Catalog (products, variants, inventory, collections, media)

Read specs/01-DATABASE-SCHEMA.md (products section), specs/05-BUSINESS-LOGIC.md (catalog/inventory), specs/09 Phase 2. Follow conventions from Phase 1 (BelongsToStore trait, money as integer cents, createStoreContext helper).

Deliver:
1. Migrations (order): products, product_options, product_option_values, product_variants, variant_option_values (composite PK), inventory_items, collections, collection_products (composite PK), product_media.
2. Models + factories + seeders: Product, ProductOption, ProductOptionValue, ProductVariant, InventoryItem, Collection, ProductMedia. Relationships per roadmap. Use BelongsToStore on store-scoped models.
3. Enums: ProductStatus(Draft,Active,Archived), VariantStatus(Active,Archived), CollectionStatus(Draft,Active,Archived), MediaType(Image,Video), MediaStatus(Processing,Ready,Failed), InventoryPolicy(Deny,Continue).
4. Services: ProductService (create/update/transitionStatus state machine/delete-guard), VariantMatrixService (rebuildMatrix cartesian product, archive orphaned-with-orders, delete orphaned-without), App\Support\HandleGenerator (unique slug per store, suffix on collision, exclude id), InventoryService (checkAvailability/reserve/release/commit/restock, transactions, InsufficientInventoryException).
5. Job App\Jobs\ProcessMediaUpload (resize thumbnail 150/medium 600/large 1200; status processing->ready/failed). Livewire WithFileUploads, public disk.
6. Pest tests: tests/Unit/HandleGeneratorTest, tests/Feature/Products/{ProductCrud,Variant,Inventory,Collection,MediaUpload}Test — match the exact test cases in specs/09 section 3. Run green + pint.

Phase 3: Themes, Pages, Navigation, Storefront layout

Read specs/04-STOREFRONT-UI.md, specs/03 (themes/pages), specs/09 Phase 3. Follow Phase 1 conventions.

Deliver:
1. Migrations + models + factories + seeders: Theme, ThemeFile, ThemeSettings (PK theme_id), Page, NavigationMenu, NavigationItem. Relationships per roadmap.
2. Enums: ThemeStatus(Draft,Published), PageStatus(Draft,Published,Archived), NavigationItemType(Link,Page,Collection,Product).
3. Storefront blade layout resources/views/storefront/: layouts/app.blade.php (header+announcement+main+footer+cart drawer, dark mode dark: prefixes), components/ (product-card, price, badge, quantity-selector, address-form, order-summary, breadcrumbs, pagination), errors/{404,503}.blade.php.
4. Services: NavigationService (buildTree, resolveUrl, 5min cache per store), ThemeSettings singleton in AppServiceProvider (loads+caches active theme settings for current store).
5. Storefront Livewire shells: Storefront\Home, Collections\Index, Collections\Show, Pages\Show. (Product\Show, Cart, Search live with their phases — coordinate, build placeholders that other phases extend.)
6. Tests for navigation/theme services + a storefront smoke (home renders). Run green + pint. NOTE: storefront resolves store by Host header — seed a store_domains row for shop.test so http://shop.test works for browser tests (coordinate with seeder owner).

Building Phase 3: themes, pages, navigation, storefront layout

Phase 4: Cart, Checkout, Discounts, Shipping, Taxes, Pricing

Read specs/05-BUSINESS-LOGIC.md (cart/checkout/pricing/discount/shipping/tax), specs/02-API-ROUTES.md (cart/checkout endpoints), specs/09 Phase 4. Follow Phase 1 conventions.

Deliver:
1. Migrations + models + factories + seeders: Cart, CartLine, Checkout, ShippingZone, ShippingRate, TaxSettings (PK store_id), Discount.
2. Enums: CartStatus(Active,Converted,Abandoned), CheckoutStatus(Started,Addressed,ShippingSelected,PaymentPending/payment_selected,Completed,Expired), DiscountType(Code,Automatic), DiscountValueType(Percent='percent',Fixed='fixed',FreeShipping='free_shipping'), DiscountStatus(Draft,Active,Expired,Disabled), ShippingRateType(Flat,Weight,Price,Carrier), TaxMode(Manual,Provider).
3. Services: CartService (create/addLine/updateLineQuantity/removeLine/getOrCreateForSession/mergeOnLogin; every mutation increments cart_version; CartVersionMismatchException on expected_version mismatch). DiscountService (validate w/ reason codes: not_found,expired,not_yet_active,usage_limit_reached,minimum_not_met; calculate w/ proportional allocation + remainder to last line; case-insensitive). ShippingCalculator (getAvailableRates zone match by country/region; calculate flat/weight/price). TaxCalculator (calculate/extractInclusive/addExclusive, integer math, basis points). PricingEngine (calculate(Checkout): PricingResult pipeline subtotal->discount->discounted->shipping->tax->total, store snapshot in totals_json). CheckoutService state machine (setAddress/setShippingMethod/selectPaymentMethod[reserve inventory]/completeCheckout[idempotent]/expireCheckout).
4. Value objects: App\ValueObjects\PricingResult, TaxLine.
5. Jobs: ExpireAbandonedCheckouts (every 15min, release inventory), CleanupAbandonedCarts (daily, mark abandoned >14d). Wire into routes/console.php.
6. Tests: tests/Unit/{PricingEngine,DiscountCalculator,TaxCalculator,ShippingCalculator,CartVersion}Test; tests/Feature/Cart/CartServiceTest; tests/Feature/Checkout/{CheckoutFlow,CheckoutState,PricingIntegration,Discount,Shipping,Tax}Test — match exact cases in specs/09. Run green + pint.

Building cart/checkout/pricing core (Phase 4)

Phase 5: Payments (Mock PSP), Orders, Refunds, Fulfillment

Read specs/05-BUSINESS-LOGIC.md (orders/payments/fulfillment/refunds), specs/09 Phase 5. Follow Phase 1 conventions. NOTE: Customer + CustomerAddress base may exist from Phase 1 (for auth) — extend, do not duplicate.

Deliver:
1. Migrations + models + factories + seeders: Customer (extend if exists), CustomerAddress (extend if exists), Order, OrderLine, Payment, Refund, Fulfillment, FulfillmentLine. Order lines store title_snapshot, sku_snapshot.
2. Enums: OrderStatus(Pending,Paid,Fulfilled,Cancelled,Refunded), FinancialStatus(Pending,Authorized,Paid,PartiallyRefunded,Refunded,Voided), FulfillmentStatus(Unfulfilled,Partial,Fulfilled), PaymentMethod(CreditCard,Paypal,BankTransfer), PaymentStatus(Pending,Captured,Failed,Refunded), RefundStatus(Pending,Processed,Failed), FulfillmentShipmentStatus(Pending,Shipped,Delivered).
3. Payments: Contract App\Contracts\PaymentProvider (charge, refund). Service App\Services\Payments\MockPaymentProvider — magic cards 4242...(success), 4000...0002(decline), 4000...9995(insufficient_funds); credit_card/paypal instant capture; bank_transfer pending; mock_ reference ids; bind interface in AppServiceProvider.
4. Services: OrderService (createFromCheckout atomic: order+lines snapshots, commit inventory, mark cart converted, dispatch OrderCreated; generateOrderNumber sequential per store #1001+; cancel guard). RefundService (create: validate <= payment, provider refund, partial->partially_refunded / full->refunded, restock flag -> InventoryService::restock). FulfillmentService (create w/ guard: financial_status must be paid|partially_refunded else FulfillmentGuardException; markAsShipped; markAsDelivered + event; update order.fulfillment_status partial/fulfilled; cannot exceed ordered qty).
5. Bank transfer: admin Confirm Payment action (validate bank_transfer+pending -> paid, capture payment, commit inventory, auto-deliver fulfillment if all digital, dispatch OrderPaid). Job CancelUnpaidBankTransferOrders (daily, cancel + release after config days).
6. Events: OrderCreated, OrderPaid, OrderFulfilled, OrderCancelled, OrderRefunded.
7. Tests: tests/Feature/Payments/{MockPaymentProvider,PaymentService,BankTransferConfirmation}Test; tests/Feature/Orders/{OrderCreation,Refund,Fulfillment}Test — match exact cases in specs/09. Run green + pint.

Building payments/orders/refunds/fulfillment (Phase 5)

Phase 3b + 6: Storefront shopping UI + Customer accounts

Read specs/04-STOREFRONT-UI.md, specs/09 Phase 3.4/4.9/6. Depends on catalog (Phase 2), pricing/cart/checkout (Phase 4), orders (Phase 5), storefront layout (Phase 3).

Deliver:
1. Storefront Livewire components (Flux UI + Tailwind v4, dark mode): Products\Show (variant select, gallery, add-to-cart), Cart\Show (full cart page), CartDrawer (slide-out: lines, qty, discount code, subtotal, checkout button — always in layout), Checkout\Show (multi-step Contact/Address -> Shipping -> Payment, stepper), Checkout\Confirmation, Search\Modal + Search\Index shells (search team fills logic). Wire storefront routes in routes/storefront.php.
2. Customer account (Phase 6): Storefront\Account\Auth\Login, Register; Account\Dashboard; Account\Orders\Index, Show; Account\Addresses\Index. Routes: /account/login, /account/register, /account, /account/orders, /account/orders/{orderNumber}, /account/addresses, POST /account/logout. Customer email unique per store; merge guest cart on login (CartService::mergeOnLogin); prevent accessing other customers' orders (404).
3. Tests: tests/Feature/Customers/{CustomerAccount,AddressManagement}Test; storefront cart/checkout feature/Livewire tests. Run green + pint.

Building storefront shopping UI + customer accounts

Phase 7: Admin Panel (full)

Read specs/03-ADMIN-UI.md, specs/09 Phase 7. Depends on catalog (Phase 2), cart/checkout (Phase 4), orders/payments/fulfillment (Phase 5). Use Flux UI Free + Tailwind v4, dark mode. Enforce policies from Phase 1.

Deliver Livewire admin components + blade layout under app/Livewire/Admin and resources/views/livewire/admin:
1. Layout: Admin\Layout\Sidebar (256px, nav groups, Flux brand/icon/separator), TopBar (store selector, profile, hamburger), Breadcrumbs. layout/app.blade.php. Toast via Livewire events.
2. Dashboard: KPI tiles (sales, order count, AOV, conversion), 30-day orders chart, recent orders table, date range filter.
3. Products: Index (search, status filter, bulk archive/delete, pagination), Form (shared create/edit: title, rich description, status, vendor, type, tags, variants builder auto-matrix, media upload reorder+alt).
4. Orders: Index (status filters, search by number/email, date range), Show (timeline, line items, payment info, fulfillment modal, refund modal, confirm bank transfer payment).
5. Collections Index/Form (product picker + reorder), Customers Index/Show, Discounts Index/Form, Settings Index (tabs: General, Domains, Shipping, Taxes, Checkout, Notifications) + Settings\Shipping (zones/rates CRUD) + Settings\Taxes, Themes Index/Editor, Pages Index/Form, Navigation Index (drag-drop), Analytics Index, Search\Settings, Apps Index/Show, Developers Index (API tokens + webhooks).
6. Wire admin routes in routes/admin.php (path /admin/*, admin middleware group, auth:web).
7. Tests: tests/Feature/Admin/{Dashboard,ProductManagement,OrderManagement,DiscountManagement,Settings}Test — match exact cases in specs/09. Run green + pint.

Phase 8/9/10: Search, Analytics, Apps/Webhooks, API + Sanctum

Read specs/02-API-ROUTES.md, specs/05-BUSINESS-LOGIC.md (search/analytics/webhooks), specs/06 (api auth), specs/09 Phases 8-10. Depends on catalog (Phase 2) + orders (Phase 5).

Deliver:
PHASE 8 Search: migrations search_settings (PK store_id), search_queries; FTS5 virtual table products_fts (raw SQL: title, description, vendor, product_type, tags synced from products). SearchService (search FTS5 store-scoped paginator, autocomplete prefix, syncProduct upsert, removeProduct). ProductObserver syncing FTS on create/update/delete. Storefront Search\Modal + Search\Index logic. Tests: tests/Feature/Search/{Search,Autocomplete}Test.
PHASE 9 Analytics: migrations analytics_events (FK store_id, customer_id), analytics_daily (composite PK store_id+date). AnalyticsService (track raw event, getDailyMetrics). Job AggregateAnalytics (daily: orders_count, revenue, aov, visits, add_to_cart, checkout_started). Event types: page_view, product_view, add_to_cart, remove_from_cart, checkout_started, checkout_completed, search. Tests: tests/Feature/Analytics/{EventIngestion,Aggregation}Test.
PHASE 10 Apps/Webhooks + API: migrations apps, app_installations, oauth_clients, oauth_tokens, webhook_subscriptions, webhook_deliveries. WebhookService (dispatch matching subs -> queue jobs; sign HMAC-SHA256; verify). Job DeliverWebhook (POST JSON, headers X-Platform-Signature/Event/Delivery-Id/Timestamp, backoff [60,300,1800,7200,43200], record delivery, circuit breaker pause after 5 fails). Sanctum personal access tokens for admin API with abilities. Build API controllers + Eloquent API Resources + versioned routes in routes/api.php: storefront API (/api/storefront/v1/carts...) rate-limited 120/min, admin API (/api/admin/v1/stores/{store}/products|orders) Bearer + abilities. Tests: tests/Feature/Webhooks/{WebhookDelivery,WebhookSignature}Test, tests/Feature/Auth/SanctumTokenTest, tests/Feature/Cart/CartApiTest, tests/Feature/Api/{StorefrontCart,StorefrontCheckout,AdminProduct,AdminOrder}ApiTest. Run green + pint. NOTE: requires laravel/sanctum — confirm with team-lead before composer require.

Building Search, Analytics, Apps/Webhooks, API + Sanctum

Phase 11b: comprehensive demo seeders + admin polish

Owner: admin. Enrich demo data so the review showcase is rich: orders across ALL statuses (pending, paid, fulfilled [partial+full], cancelled, refunded [partial+full], bank-transfer-pending to demo Confirm Payment), a few customers with addresses + order history, discounts/collections/shipping/tax already present. Ensure products have media that RENDERS in the browser (generate lightweight placeholder images into storage/app/public via GD, or ensure a clean image fallback in product-card). Keep seeders idempotent for migrate:fresh --seed. Admin-side a11y/responsive/dark polish pass. Full suite green + pint + migrate:fresh --seed clean.

Enriching demo seeders and polishing admin UI

Phase 12: Full verification + review meeting (team-lead)

Team-lead owns. After all phases: run full Pest suite green (php artisan test), pint clean, migrate:fresh --seed clean, then Playwright MCP browser verification of storefront (browse products, add to cart, checkout end-to-end) and admin (login, manage products, view/fulfill/refund orders, confirm bank transfer). Verify all acceptance criteria across specs. Fix any bugs found (assign to owners). Then run the review meeting / feature showcase for the user.

Reconcile route naming + fix CheckoutComponentTest

Settle on one route-name scheme (storefront.cart / storefront.checkout / storefront.checkout.confirmation), add real routes to routes/storefront.php, update commerce component redirects + CheckoutComponentTest stubs to match, and remove the fragile runtime Route::get stubs from CheckoutComponentTest beforeEach. Full suite must be green.

Reconciling routes and fixing CheckoutComponentTest

Build Products\Show product page

Storefront\Products\Show Livewire component + view: image gallery, variant selector, price w/ compare-at, quantity, add-to-cart (dispatch add-to-cart to CartDrawer), stock messaging via catalog purchasable/inventory, breadcrumbs, description, tags. Route /products/{handle} name storefront.products.show.

Polish CartDrawer + Cart\Show views

Polish commerce's cart-drawer + cart/show blade views to spec: line items w/ thumbnails, quantity-selector component, remove, discount code input, totals, checkout button, empty state, ARIA dialog/focus. Reuse storefront components.

Polish Checkout\Show + Confirmation views

Polish commerce's checkout/show + checkout/confirmation views: multi-step stepper (Contact/Address, Shipping, Payment), order-summary sidebar, card form (4242), bank transfer instructions on confirmation. Align order-summary keys to checkout totals_json.

Build Search Modal + Index shells

Storefront\Search\Modal (listens open-search-modal, autocomplete UI shell) + Search\Index (results page shell reusing collection grid). Coordinate with platform for SearchService logic in #8 — provide UI + hooks only.

Build customer account pages

Account\Dashboard (recent orders + quick links), Account\Orders\Index (paginated), Account\Orders\Show (timeline, 404 on other customers' orders), Account\Addresses\Index (CRUD + set default). Enhance existing Login/Register to spec. Routes: /account, /account/orders, /account/orders/{orderNumber}, /account/addresses, POST logout.

Write tests + browser-verify full flow

tests/Feature/Customers/{CustomerAccountTest,AddressManagementTest} + storefront product/cart/checkout Livewire tests. Full suite green + pint. Browser-verify full purchase flow (browse->product->add->checkout->pay 4242->confirmation) + account pages at shop.test.

Fix tenancy: re-bind current_store on Livewire update requests

Foundation-level bug found by storefront: on /livewire/update requests, nested/global Livewire components (e.g. layout CartDrawer) 500 with "Target class [current_store] does not exist" because ResolveStore (registered as storefront/admin route-group middleware) does not re-run on the shared Livewire update endpoint. Fix: Livewire::addPersistentMiddleware(ResolveStore::class) + make ResolveStore self-detect storefront-vs-admin mode when invoked without the mode arg (persistent middleware drops args). Add a regression test exercising a nested component update path. Affects storefront (#6) + admin (#7). Owner: foundation.

Fixing current_store binding on Livewire updates

Phase 11a: storefront polish (branded auth, a11y, dark/responsive, 404/503)

Storefront-side polish for Phase 11. Owner: storefront. Move customer Login/Register from the Flux auth layout to the storefront-branded layout. Accessibility pass (skip links, ARIA, focus management in cart drawer + modals + checkout stepper). Verify dark mode + responsive (sm/md/lg/xl) across storefront pages; fill gaps. Confirm styled 404/503 render at shop.test. Browser-verify key pages. Full suite green + pint clean.

Awaiting team-lead steer on 404/503 handler wiring (items 1-3 done)


### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
```yaml
- generic [ref=e136]:
  - generic [ref=e137]:
    - generic [ref=e138]:
      - link [ref=e139] [cursor=pointer]:
        - /url: http://shop.test/admin
        - img [ref=e140]
      - img [ref=e142]
    - generic [ref=e144]:
      - link "Orders" [ref=e145] [cursor=pointer]:
        - /url: http://shop.test/admin/orders
      - img [ref=e146]
    - generic [ref=e149]: "#1003"
  - generic [ref=e150]:
    - generic [ref=e151]:
      - generic [ref=e152]:
        - generic [ref=e153]:
          - heading "#1003" [level=1] [ref=e154]
          - generic [ref=e155]: Pending
          - generic [ref=e156]: Unfulfilled
        - paragraph [ref=e157]: May 27, 2026 2:33 PM
        - button "Confirm payment" [ref=e159]:
          - img [ref=e161]
          - img [ref=e164]
          - generic [ref=e166]: Confirm payment
        - generic [ref=e167]:
          - img [ref=e169]
          - generic [ref=e172]:
            - generic [ref=e173]: Cannot create fulfillment
            - generic [ref=e174]:
              - text: "Payment must be confirmed before items can be fulfilled. Current financial status:"
              - strong [ref=e175]: pending
              - text: .
      - generic [ref=e176]:
        - generic [ref=e177]: Timeline
        - list [ref=e178]:
          - listitem [ref=e179]:
            - paragraph [ref=e181]: Order placed
            - paragraph [ref=e182]: May 27, 2026 2:33 PM
      - generic [ref=e183]:
        - generic [ref=e184]: Order lines
        - table [ref=e187]:
          - rowgroup [ref=e188]:
            - row "Product Qty Unit Total" [ref=e189]:
              - columnheader "Product" [ref=e190]:
                - generic [ref=e191]: Product
              - columnheader "Qty" [ref=e192]:
                - generic [ref=e193]: Qty
              - columnheader "Unit" [ref=e194]:
                - generic [ref=e195]: Unit
              - columnheader "Total" [ref=e196]:
                - generic [ref=e197]: Total
          - rowgroup [ref=e198]:
            - row "Classic Cotton T-Shirt 2 29.00 USD 58.00 USD" [ref=e199]:
              - cell "Classic Cotton T-Shirt" [ref=e200]:
                - generic [ref=e201]: Classic Cotton T-Shirt
              - cell "2" [ref=e202]
              - cell "29.00 USD" [ref=e203]
              - cell "58.00 USD" [ref=e204]
            - row "Classic Cotton T-Shirt 2 30.00 USD 60.00 USD" [ref=e205]:
              - cell "Classic Cotton T-Shirt" [ref=e206]:
                - generic [ref=e207]: Classic Cotton T-Shirt
              - cell "2" [ref=e208]
              - cell "30.00 USD" [ref=e209]
              - cell "60.00 USD" [ref=e210]
        - generic [ref=e211]:
          - generic [ref=e212]:
            - generic [ref=e213]: Subtotal
            - generic [ref=e214]: 118.00 USD
          - generic [ref=e215]:
            - generic [ref=e216]: Shipping
            - generic [ref=e217]: 5.99 USD
          - generic [ref=e218]:
            - generic [ref=e219]: Tax
            - generic [ref=e220]: 9.44 USD
          - generic [ref=e221]:
            - generic [ref=e222]: Total
            - generic [ref=e223]: 133.43 USD
      - generic [ref=e224]:
        - generic [ref=e225]: Payment details
        - generic [ref=e226]:
          - generic [ref=e227]: Bank transfer
          - generic [ref=e228]: Pending
          - generic [ref=e229]: 133.43 USD
          - generic [ref=e230]: mock_5tnzt8kdxdivrt6f65lrgkhq
        - button "Confirm payment" [ref=e232]:
          - img [ref=e234]
          - generic [ref=e237]: Confirm payment
    - generic [ref=e238]:
      - generic [ref=e239]:
        - generic [ref=e240]: Customer
        - paragraph [ref=e241]: Liam Müller
        - paragraph [ref=e242]: liam.mueller@example.de
        - link "View customer" [ref=e243] [cursor=pointer]:
          - /url: http://shop.test/admin/customers/3
      - generic [ref=e244]:
        - generic [ref=e245]: Shipping address
        - generic [ref=e246]:
          - text: Liam Müller
          - text: Hauptstrasse 5
          - text: Berlin 10115
          - text: DE
      - generic [ref=e247]:
        - generic [ref=e248]: Billing address
        - generic [ref=e249]:
          - text: Liam Müller
          - text: Hauptstrasse 5
          - text: Berlin 10115
          - text: DE
```


Order #1003 detail is thorough — status badges, the **fulfillment guard correctly surfaced** ("Payment must be confirmed before items can be fulfilled. Current financial status: pending"), order lines, totals (118.00 + 5.99 + 9.44 = 133.43), bank-transfer payment details, customer + addresses. Let me click **Confirm payment** (dispatches `OrderPaid` → webhook — re-tests the fix covers all order events).


### Ran Playwright code
```js
await page.getByRole('button', { name: 'Confirm payment' }).nth(1).click();
```
### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-37-16-133Z.yml)


### Result
Total messages: 3 (Errors: 0, Warnings: 0)
Returning 0 messages for level "error"


### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
```yaml
- generic [ref=e150]:
  - generic [ref=e151]:
    - generic [ref=e152]:
      - generic [ref=e153]:
        - heading "#1003" [level=1] [ref=e154]
        - generic [ref=e155]: Paid
        - generic [ref=e156]: Unfulfilled
      - paragraph [ref=e157]: May 27, 2026 2:33 PM
      - generic [ref=e158]:
        - button "Create fulfillment" [ref=e250]:
          - img [ref=e252]
          - img [ref=e255]
          - generic [ref=e259]: Create fulfillment
        - button "Refund" [ref=e260]:
          - img [ref=e262]
          - img [ref=e265]
          - generic [ref=e267]: Refund
    - generic [ref=e176]:
      - generic [ref=e177]: Timeline
      - list [ref=e178]:
        - listitem [ref=e179]:
          - paragraph [ref=e181]: Order placed
          - paragraph [ref=e182]: May 27, 2026 2:33 PM
        - listitem [ref=e268]:
          - paragraph [ref=e270]: Payment received
          - paragraph [ref=e271]: May 31, 2026 2:33 PM
    - generic [ref=e183]:
      - generic [ref=e184]: Order lines
      - table [ref=e187]:
        - rowgroup [ref=e188]:
          - row "Product Qty Unit Total" [ref=e189]:
            - columnheader "Product" [ref=e190]:
              - generic [ref=e191]: Product
            - columnheader "Qty" [ref=e192]:
              - generic [ref=e193]: Qty
            - columnheader "Unit" [ref=e194]:
              - generic [ref=e195]: Unit
            - columnheader "Total" [ref=e196]:
              - generic [ref=e197]: Total
        - rowgroup [ref=e198]:
          - row "Classic Cotton T-Shirt 2 29.00 USD 58.00 USD" [ref=e199]:
            - cell "Classic Cotton T-Shirt" [ref=e200]:
              - generic [ref=e201]: Classic Cotton T-Shirt
            - cell "2" [ref=e202]
            - cell "29.00 USD" [ref=e203]
            - cell "58.00 USD" [ref=e204]
          - row "Classic Cotton T-Shirt 2 30.00 USD 60.00 USD" [ref=e205]:
            - cell "Classic Cotton T-Shirt" [ref=e206]:
              - generic [ref=e207]: Classic Cotton T-Shirt
            - cell "2" [ref=e208]
            - cell "30.00 USD" [ref=e209]
            - cell "60.00 USD" [ref=e210]
      - generic [ref=e211]:
        - generic [ref=e212]:
          - generic [ref=e213]: Subtotal
          - generic [ref=e214]: 118.00 USD
        - generic [ref=e215]:
          - generic [ref=e216]: Shipping
          - generic [ref=e217]: 5.99 USD
        - generic [ref=e218]:
          - generic [ref=e219]: Tax
          - generic [ref=e220]: 9.44 USD
        - generic [ref=e221]:
          - generic [ref=e222]: Total
          - generic [ref=e223]: 133.43 USD
    - generic [ref=e224]:
      - generic [ref=e225]: Payment details
      - generic [ref=e226]:
        - generic [ref=e227]: Bank transfer
        - generic [ref=e228]: Captured
        - generic [ref=e229]: 133.43 USD
        - generic [ref=e230]: mock_5tnzt8kdxdivrt6f65lrgkhq
  - generic [ref=e238]:
    - generic [ref=e239]:
      - generic [ref=e240]: Customer
      - paragraph [ref=e241]: Liam Müller
      - paragraph [ref=e242]: liam.mueller@example.de
      - link "View customer" [ref=e243] [cursor=pointer]:
        - /url: http://shop.test/admin/customers/3
    - generic [ref=e244]:
      - generic [ref=e245]: Shipping address
      - generic [ref=e246]:
        - text: Liam Müller
        - text: Hauptstrasse 5
        - text: Berlin 10115
        - text: DE
    - generic [ref=e247]:
      - generic [ref=e248]: Billing address
      - generic [ref=e249]:
        - text: Liam Müller
        - text: Hauptstrasse 5
        - text: Berlin 10115
        - text: DE
```


**Confirm Payment works flawlessly** — status flipped Pending → **Paid**, payment **Captured**, "Payment received" added to the timeline, the fulfillment guard cleared and **Create fulfillment + Refund** buttons now appear, **0 console errors** (the `OrderPaid` webhook handled gracefully — confirming the fix covers all order events). Let me test Create fulfillment.


### Ran Playwright code
```js
await page.locator('[data-test="open-fulfillment"]').click();
```
### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-37-35-459Z.yml)


### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
```yaml
- generic [active] [ref=e1]:
  - generic [ref=e3]:
    - link "Acme Fashion" [ref=e5] [cursor=pointer]:
      - /url: http://shop.test/admin
      - img [ref=e8]
      - generic [ref=e10]: Acme Fashion
    - navigation [ref=e11]:
      - link "Dashboard" [ref=e13] [cursor=pointer]:
        - /url: http://shop.test/admin
        - img [ref=e15]
        - generic [ref=e17]: Dashboard
      - generic [ref=e18]:
        - generic [ref=e20]: Products
        - generic [ref=e21]:
          - link "Products" [ref=e23] [cursor=pointer]:
            - /url: http://shop.test/admin/products
            - img [ref=e25]
            - generic [ref=e27]: Products
          - link "Collections" [ref=e29] [cursor=pointer]:
            - /url: http://shop.test/admin/collections
            - img [ref=e31]
            - generic [ref=e33]: Collections
          - link "Inventory" [ref=e35] [cursor=pointer]:
            - /url: http://shop.test/admin/inventory
            - img [ref=e37]
            - generic [ref=e39]: Inventory
      - generic [ref=e40]:
        - generic [ref=e42]: Orders
        - link "Orders" [ref=e45] [cursor=pointer]:
          - /url: http://shop.test/admin/orders
          - img [ref=e47]
          - generic [ref=e49]: Orders
      - generic [ref=e50]:
        - generic [ref=e52]: Customers
        - link "Customers" [ref=e55] [cursor=pointer]:
          - /url: http://shop.test/admin/customers
          - img [ref=e57]
          - generic [ref=e59]: Customers
      - generic [ref=e60]:
        - generic [ref=e62]: Discounts
        - link "Discounts" [ref=e65] [cursor=pointer]:
          - /url: http://shop.test/admin/discounts
          - img [ref=e67]
          - generic [ref=e70]: Discounts
      - generic [ref=e71]:
        - generic [ref=e73]: Content
        - generic [ref=e74]:
          - link "Pages" [ref=e76] [cursor=pointer]:
            - /url: http://shop.test/admin/pages
            - img [ref=e78]
            - generic [ref=e80]: Pages
          - link "Navigation" [ref=e82] [cursor=pointer]:
            - /url: http://shop.test/admin/navigation
            - img [ref=e84]
            - generic [ref=e86]: Navigation
          - link "Themes" [ref=e88] [cursor=pointer]:
            - /url: http://shop.test/admin/themes
            - img [ref=e90]
            - generic [ref=e92]: Themes
    - navigation [ref=e93]:
      - link "Analytics" [ref=e95] [cursor=pointer]:
        - /url: http://shop.test/admin/analytics
        - img [ref=e97]
        - generic [ref=e100]: Analytics
      - link "Settings" [ref=e102] [cursor=pointer]:
        - /url: http://shop.test/admin/settings
        - img [ref=e104]
        - generic [ref=e107]: Settings
      - link "Apps" [ref=e109] [cursor=pointer]:
        - /url: http://shop.test/admin/apps
        - img [ref=e111]
        - generic [ref=e113]: Apps
      - link "Developers" [ref=e115] [cursor=pointer]:
        - /url: http://shop.test/admin/developers
        - img [ref=e117]
        - generic [ref=e119]: Developers
  - banner [ref=e121]:
    - paragraph [ref=e122]: Acme Fashion
    - button "Notifications" [ref=e123]:
      - img [ref=e124]
    - button "DO Demo Owner" [ref=e127]:
      - generic [ref=e130]: DO
      - generic [ref=e131]: Demo Owner
      - img [ref=e133]
  - generic [ref=e136]:
    - generic [ref=e137]:
      - generic [ref=e138]:
        - link [ref=e139] [cursor=pointer]:
          - /url: http://shop.test/admin
          - img [ref=e140]
        - img [ref=e142]
      - generic [ref=e144]:
        - link "Orders" [ref=e145] [cursor=pointer]:
          - /url: http://shop.test/admin/orders
        - img [ref=e146]
      - generic [ref=e149]: "#1003"
    - generic [ref=e150]:
      - generic [ref=e151]:
        - generic [ref=e152]:
          - generic [ref=e153]:
            - heading "#1003" [level=1] [ref=e154]
            - generic [ref=e155]: Paid
            - generic [ref=e156]: Unfulfilled
          - paragraph [ref=e157]: May 27, 2026 2:33 PM
          - generic [ref=e158]:
            - button "Create fulfillment" [ref=e250]
            - button "Refund" [ref=e260]
        - generic [ref=e176]:
          - generic [ref=e177]: Timeline
          - list [ref=e178]:
            - listitem [ref=e179]
            - listitem [ref=e268]
        - generic [ref=e183]:
          - generic [ref=e184]: Order lines
          - table [ref=e187]:
            - rowgroup [ref=e188]
            - rowgroup [ref=e198]
          - generic [ref=e211]:
            - generic [ref=e212]
            - generic [ref=e215]
            - generic [ref=e218]
            - generic [ref=e221]
        - generic [ref=e224]:
          - generic [ref=e225]: Payment details
          - generic [ref=e226]:
            - generic [ref=e227]: Bank transfer
            - generic [ref=e228]: Captured
            - generic [ref=e229]: 133.43 USD
            - generic [ref=e230]: mock_5tnzt8kdxdivrt6f65lrgkhq
      - generic [ref=e238]:
        - generic [ref=e239]:
          - generic [ref=e240]: Customer
          - paragraph [ref=e241]: Liam Müller
          - paragraph [ref=e242]: liam.mueller@example.de
          - link "View customer" [ref=e243] [cursor=pointer]:
            - /url: http://shop.test/admin/customers/3
        - generic [ref=e244]:
          - generic [ref=e245]: Shipping address
          - generic [ref=e246]:
            - text: Liam Müller
            - text: Hauptstrasse 5
            - text: Berlin 10115
            - text: DE
        - generic [ref=e247]:
          - generic [ref=e248]: Billing address
          - generic [ref=e249]:
            - text: Liam Müller
            - text: Hauptstrasse 5
            - text: Berlin 10115
            - text: DE
    - dialog [ref=e279]:
      - generic [ref=e280]:
        - generic [ref=e281]: Create fulfillment
        - generic [ref=e282]:
          - generic [ref=e283]:
            - generic [ref=e284]
            - spinbutton [ref=e288]: "2"
          - generic [ref=e289]:
            - generic [ref=e290]
            - spinbutton [ref=e294]: "2"
        - generic [ref=e295]:
          - generic [ref=e296]: Tracking company
          - textbox "Tracking company" [ref=e298]:
            - /placeholder: UPS, FedEx, DHL...
        - generic [ref=e299]:
          - generic [ref=e300]: Tracking number
          - textbox "Tracking number" [ref=e302]
        - generic [ref=e303]:
          - generic [ref=e304]: Tracking URL
          - textbox "Tracking URL" [ref=e306]
        - generic [ref=e307]:
          - button "Cancel" [ref=e308]:
            - img [ref=e310]
            - generic [ref=e313]: Cancel
          - button "Create fulfillment" [ref=e314]:
            - img [ref=e316]
            - generic [ref=e319]: Create fulfillment
      - button "Close modal" [ref=e322]:
        - img [ref=e323]
```


The fulfillment modal works — line quantities (2 + 2) and tracking fields. Let me add tracking and submit.


DHL


### Ran Playwright code
```js
await page.getByRole('textbox', { name: 'Tracking company' }).fill('DHL');
```


CRITICAL — blocks everything. Read specs/01-DATABASE-SCHEMA.md, specs/06-AUTH-AND-SECURITY.md, and specs/09-IMPLEMENTATION-ROADMAP.md Phase 1.

Deliver:
1. Config: add `customer` guard + `customers` provider + `customers` password broker to config/auth.php (Customer model). SQLite tuning in config/database.php (WAL, foreign_keys, busy_timeout=5000, synchronous=normal). JSON structured logging channel in config/logging.php.
2. Migrations (dependency order): create_organizations_table, create_stores_table (FK organization_id), create_store_domains_table (FK store_id), modify users table (add status, last_login_at; keep two_factor cols), create_store_users_table (composite store_id+user_id, role), create_store_settings_table (PK store_id). Money = INTEGER minor units. Enums = TEXT with CHECK. Timestamps ISO-8601.
3. Models + factories + seeders: Organization, Store, StoreDomain, StoreUser (custom Pivot w/ role), StoreSettings. Relationships per roadmap. Explicit return types, casts() method, $fillable.
4. Enums in app/Enums/: StoreStatus(Active,Suspended), StoreUserRole(Owner,Admin,Staff,Support), StoreDomainType(Storefront,Admin,Api). TitleCase keys.
5. Middleware App\Http\Middleware\ResolveStore: storefront resolves Store by Host header from store_domains (cache hostname->store_id 5min, bind singleton container 'current_store'); admin reads session current_store_id + verifies store_users; 404 unknown host; 503 suspended. Register `storefront` and `admin` middleware groups in bootstrap/app.php.
6. Trait App\Models\Concerns\BelongsToStore + Scope App\Models\Scopes\StoreScope (where store_id = app('current_store')->id; auto-set store_id on creating).
7. Admin auth: Livewire Admin\Auth\Login + Logout, Auth::guard('web')->attempt, rate limit 5/min/IP (RateLimiter::for('login') in AppServiceProvider), password reset via Password::broker('users'), record last_login_at, regenerate session.
8. Customer auth: customer guard, CustomerUserProvider injecting store_id, email unique per store. (Customer model lives in Phase 5 — coordinate: create a minimal Customer model + migration here OR leave provider ready; create customers table migration here so guard works. Add customers + customer_addresses migrations + Customer model here to unblock auth, Phase 5 extends.)
9. Policies for Product, Order, Collection, Discount, Customer, Store, Page, Theme, Fulfillment, Refund using User::roleForStore(Store): ?StoreUserRole + permission matrix in roadmap. Register via AppServiceProvider.
10. Pest helpers in tests/Pest.php (or a trait): createStoreContext() (Organization+Store+StoreDomain+Owner User, binds current_store), actingAsAdmin(User), actingAsCustomer(Customer).
11. Route skeleton: routes/storefront.php, routes/admin.php, routes/api.php (empty stubs with middleware groups), wire into bootstrap/app.php. Replace welcome route. Remove/disable starter-kit tests that no longer apply (DashboardTest, Settings/* if they conflict) — but keep auth working.

Write Pest tests for: TenantResolution, StoreIsolation, AdminAuth, CustomerAuth. Run them green. Run `vendor/bin/pint --dirty --format agent`. Report conventions (base model pattern, money casting, how to use createStoreContext) back to team-lead so other domains follow them.

Read specs/01-DATABASE-SCHEMA.md (products section), specs/05-BUSINESS-LOGIC.md (catalog/inventory), specs/09 Phase 2. Follow conventions from Phase 1 (BelongsToStore trait, money as integer cents, createStoreContext helper).

Deliver:
1. Migrations (order): products, product_options, product_option_values, product_variants, variant_option_values (composite PK), inventory_items, collections, collection_products (composite PK), product_media.
2. Models + factories + seeders: Product, ProductOption, ProductOptionValue, ProductVariant, InventoryItem, Collection, ProductMedia. Relationships per roadmap. Use BelongsToStore on store-scoped models.
3. Enums: ProductStatus(Draft,Active,Archived), VariantStatus(Active,Archived), CollectionStatus(Draft,Active,Archived), MediaType(Image,Video), MediaStatus(Processing,Ready,Failed), InventoryPolicy(Deny,Continue).
4. Services: ProductService (create/update/transitionStatus state machine/delete-guard), VariantMatrixService (rebuildMatrix cartesian product, archive orphaned-with-orders, delete orphaned-without), App\Support\HandleGenerator (unique slug per store, suffix on collision, exclude id), InventoryService (checkAvailability/reserve/release/commit/restock, transactions, InsufficientInventoryException).
5. Job App\Jobs\ProcessMediaUpload (resize thumbnail 150/medium 600/large 1200; status processing->ready/failed). Livewire WithFileUploads, public disk.
6. Pest tests: tests/Unit/HandleGeneratorTest, tests/Feature/Products/{ProductCrud,Variant,Inventory,Collection,MediaUpload}Test — match the exact test cases in specs/09 section 3. Run green + pint.

Read specs/04-STOREFRONT-UI.md, specs/03 (themes/pages), specs/09 Phase 3. Follow Phase 1 conventions.

Deliver:
1. Migrations + models + factories + seeders: Theme, ThemeFile, ThemeSettings (PK theme_id), Page, NavigationMenu, NavigationItem. Relationships per roadmap.
2. Enums: ThemeStatus(Draft,Published), PageStatus(Draft,Published,Archived), NavigationItemType(Link,Page,Collection,Product).
3. Storefront blade layout resources/views/storefront/: layouts/app.blade.php (header+announcement+main+footer+cart drawer, dark mode dark: prefixes), components/ (product-card, price, badge, quantity-selector, address-form, order-summary, breadcrumbs, pagination), errors/{404,503}.blade.php.
4. Services: NavigationService (buildTree, resolveUrl, 5min cache per store), ThemeSettings singleton in AppServiceProvider (loads+caches active theme settings for current store).
5. Storefront Livewire shells: Storefront\Home, Collections\Index, Collections\Show, Pages\Show. (Product\Show, Cart, Search live with their phases — coordinate, build placeholders that other phases extend.)
6. Tests for navigation/theme services + a storefront smoke (home renders). Run green + pint. NOTE: storefront resolves store by Host header — seed a store_domains row for shop.test so http://shop.test works for browser tests (coordinate with seeder owner).

Read specs/05-BUSINESS-LOGIC.md (cart/checkout/pricing/discount/shipping/tax), specs/02-API-ROUTES.md (cart/checkout endpoints), specs/09 Phase 4. Follow Phase 1 conventions.

Deliver:
1. Migrations + models + factories + seeders: Cart, CartLine, Checkout, ShippingZone, ShippingRate, TaxSettings (PK store_id), Discount.
2. Enums: CartStatus(Active,Converted,Abandoned), CheckoutStatus(Started,Addressed,ShippingSelected,PaymentPending/payment_selected,Completed,Expired), DiscountType(Code,Automatic), DiscountValueType(Percent='percent',Fixed='fixed',FreeShipping='free_shipping'), DiscountStatus(Draft,Active,Expired,Disabled), ShippingRateType(Flat,Weight,Price,Carrier), TaxMode(Manual,Provider).
3. Services: CartService (create/addLine/updateLineQuantity/removeLine/getOrCreateForSession/mergeOnLogin; every mutation increments cart_version; CartVersionMismatchException on expected_version mismatch). DiscountService (validate w/ reason codes: not_found,expired,not_yet_active,usage_limit_reached,minimum_not_met; calculate w/ proportional allocation + remainder to last line; case-insensitive). ShippingCalculator (getAvailableRates zone match by country/region; calculate flat/weight/price). TaxCalculator (calculate/extractInclusive/addExclusive, integer math, basis points). PricingEngine (calculate(Checkout): PricingResult pipeline subtotal->discount->discounted->shipping->tax->total, store snapshot in totals_json). CheckoutService state machine (setAddress/setShippingMethod/selectPaymentMethod[reserve inventory]/completeCheckout[idempotent]/expireCheckout).
4. Value objects: App\ValueObjects\PricingResult, TaxLine.
5. Jobs: ExpireAbandonedCheckouts (every 15min, release inventory), CleanupAbandonedCarts (daily, mark abandoned >14d). Wire into routes/console.php.
6. Tests: tests/Unit/{PricingEngine,DiscountCalculator,TaxCalculator,ShippingCalculator,CartVersion}Test; tests/Feature/Cart/CartServiceTest; tests/Feature/Checkout/{CheckoutFlow,CheckoutState,PricingIntegration,Discount,Shipping,Tax}Test — match exact cases in specs/09. Run green + pint.

Read specs/05-BUSINESS-LOGIC.md (orders/payments/fulfillment/refunds), specs/09 Phase 5. Follow Phase 1 conventions. NOTE: Customer + CustomerAddress base may exist from Phase 1 (for auth) — extend, do not duplicate.

Deliver:
1. Migrations + models + factories + seeders: Customer (extend if exists), CustomerAddress (extend if exists), Order, OrderLine, Payment, Refund, Fulfillment, FulfillmentLine. Order lines store title_snapshot, sku_snapshot.
2. Enums: OrderStatus(Pending,Paid,Fulfilled,Cancelled,Refunded), FinancialStatus(Pending,Authorized,Paid,PartiallyRefunded,Refunded,Voided), FulfillmentStatus(Unfulfilled,Partial,Fulfilled), PaymentMethod(CreditCard,Paypal,BankTransfer), PaymentStatus(Pending,Captured,Failed,Refunded), RefundStatus(Pending,Processed,Failed), FulfillmentShipmentStatus(Pending,Shipped,Delivered).
3. Payments: Contract App\Contracts\PaymentProvider (charge, refund). Service App\Services\Payments\MockPaymentProvider — magic cards 4242...(success), 4000...0002(decline), 4000...9995(insufficient_funds); credit_card/paypal instant capture; bank_transfer pending; mock_ reference ids; bind interface in AppServiceProvider.
4. Services: OrderService (createFromCheckout atomic: order+lines snapshots, commit inventory, mark cart converted, dispatch OrderCreated; generateOrderNumber sequential per store #1001+; cancel guard). RefundService (create: validate <= payment, provider refund, partial->partially_refunded / full->refunded, restock flag -> InventoryService::restock). FulfillmentService (create w/ guard: financial_status must be paid|partially_refunded else FulfillmentGuardException; markAsShipped; markAsDelivered + event; update order.fulfillment_status partial/fulfilled; cannot exceed ordered qty).
5. Bank transfer: admin Confirm Payment action (validate bank_transfer+pending -> paid, capture payment, commit inventory, auto-deliver fulfillment if all digital, dispatch OrderPaid). Job CancelUnpaidBankTransferOrders (daily, cancel + release after config days).
6. Events: OrderCreated, OrderPaid, OrderFulfilled, OrderCancelled, OrderRefunded.
7. Tests: tests/Feature/Payments/{MockPaymentProvider,PaymentService,BankTransferConfirmation}Test; tests/Feature/Orders/{OrderCreation,Refund,Fulfillment}Test — match exact cases in specs/09. Run green + pint.

Read specs/04-STOREFRONT-UI.md, specs/09 Phase 3.4/4.9/6. Depends on catalog (Phase 2), pricing/cart/checkout (Phase 4), orders (Phase 5), storefront layout (Phase 3).

Deliver:
1. Storefront Livewire components (Flux UI + Tailwind v4, dark mode): Products\Show (variant select, gallery, add-to-cart), Cart\Show (full cart page), CartDrawer (slide-out: lines, qty, discount code, subtotal, checkout button — always in layout), Checkout\Show (multi-step Contact/Address -> Shipping -> Payment, stepper), Checkout\Confirmation, Search\Modal + Search\Index shells (search team fills logic). Wire storefront routes in routes/storefront.php.
2. Customer account (Phase 6): Storefront\Account\Auth\Login, Register; Account\Dashboard; Account\Orders\Index, Show; Account\Addresses\Index. Routes: /account/login, /account/register, /account, /account/orders, /account/orders/{orderNumber}, /account/addresses, POST /account/logout. Customer email unique per store; merge guest cart on login (CartService::mergeOnLogin); prevent accessing other customers' orders (404).
3. Tests: tests/Feature/Customers/{CustomerAccount,AddressManagement}Test; storefront cart/checkout feature/Livewire tests. Run green + pint.

Phase 7: Admin Panel (full)

Read specs/03-ADMIN-UI.md, specs/09 Phase 7. Depends on catalog (Phase 2), cart/checkout (Phase 4), orders/payments/fulfillment (Phase 5). Use Flux UI Free + Tailwind v4, dark mode. Enforce policies from Phase 1.

Deliver Livewire admin components + blade layout under app/Livewire/Admin and resources/views/livewire/admin:
1. Layout: Admin\Layout\Sidebar (256px, nav groups, Flux brand/icon/separator), TopBar (store selector, profile, hamburger), Breadcrumbs. layout/app.blade.php. Toast via Livewire events.
2. Dashboard: KPI tiles (sales, order count, AOV, conversion), 30-day orders chart, recent orders table, date range filter.
3. Products: Index (search, status filter, bulk archive/delete, pagination), Form (shared create/edit: title, rich description, status, vendor, type, tags, variants builder auto-matrix, media upload reorder+alt).
4. Orders: Index (status filters, search by number/email, date range), Show (timeline, line items, payment info, fulfillment modal, refund modal, confirm bank transfer payment).
5. Collections Index/Form (product picker + reorder), Customers Index/Show, Discounts Index/Form, Settings Index (tabs: General, Domains, Shipping, Taxes, Checkout, Notifications) + Settings\Shipping (zones/rates CRUD) + Settings\Taxes, Themes Index/Editor, Pages Index/Form, Navigation Index (drag-drop), Analytics Index, Search\Settings, Apps Index/Show, Developers Index (API tokens + webhooks).
6. Wire admin routes in routes/admin.php (path /admin/*, admin middleware group, auth:web).
7. Tests: tests/Feature/Admin/{Dashboard,ProductManagement,OrderManagement,DiscountManagement,Settings}Test — match exact cases in specs/09. Run green + pint.

Read specs/02-API-ROUTES.md, specs/05-BUSINESS-LOGIC.md (search/analytics/webhooks), specs/06 (api auth), specs/09 Phases 8-10. Depends on catalog (Phase 2) + orders (Phase 5).

Deliver:
PHASE 8 Search: migrations search_settings (PK store_id), search_queries; FTS5 virtual table products_fts (raw SQL: title, description, vendor, product_type, tags synced from products). SearchService (search FTS5 store-scoped paginator, autocomplete prefix, syncProduct upsert, removeProduct). ProductObserver syncing FTS on create/update/delete. Storefront Search\Modal + Search\Index logic. Tests: tests/Feature/Search/{Search,Autocomplete}Test.
PHASE 9 Analytics: migrations analytics_events (FK store_id, customer_id), analytics_daily (composite PK store_id+date). AnalyticsService (track raw event, getDailyMetrics). Job AggregateAnalytics (daily: orders_count, revenue, aov, visits, add_to_cart, checkout_started). Event types: page_view, product_view, add_to_cart, remove_from_cart, checkout_started, checkout_completed, search. Tests: tests/Feature/Analytics/{EventIngestion,Aggregation}Test.
PHASE 10 Apps/Webhooks + API: migrations apps, app_installations, oauth_clients, oauth_tokens, webhook_subscriptions, webhook_deliveries. WebhookService (dispatch matching subs -> queue jobs; sign HMAC-SHA256; verify). Job DeliverWebhook (POST JSON, headers X-Platform-Signature/Event/Delivery-Id/Timestamp, backoff [60,300,1800,7200,43200], record delivery, circuit breaker pause after 5 fails). Sanctum personal access tokens for admin API with abilities. Build API controllers + Eloquent API Resources + versioned routes in routes/api.php: storefront API (/api/storefront/v1/carts...) rate-limited 120/min, admin API (/api/admin/v1/stores/{store}/products|orders) Bearer + abilities. Tests: tests/Feature/Webhooks/{WebhookDelivery,WebhookSignature}Test, tests/Feature/Auth/SanctumTokenTest, tests/Feature/Cart/CartApiTest, tests/Feature/Api/{StorefrontCart,StorefrontCheckout,AdminProduct,AdminOrder}ApiTest. Run green + pint. NOTE: requires laravel/sanctum — confirm with team-lead before composer require.

Owner: admin. Enrich demo data so the review showcase is rich: orders across ALL statuses (pending, paid, fulfilled [partial+full], cancelled, refunded [partial+full], bank-transfer-pending to demo Confirm Payment), a few customers with addresses + order history, discounts/collections/shipping/tax already present. Ensure products have media that RENDERS in the browser (generate lightweight placeholder images into storage/app/public via GD, or ensure a clean image fallback in product-card). Keep seeders idempotent for migrate:fresh --seed. Admin-side a11y/responsive/dark polish pass. Full suite green + pint + migrate:fresh --seed clean.

Team-lead owns. After all phases: run full Pest suite green (php artisan test), pint clean, migrate:fresh --seed clean, then Playwright MCP browser verification of storefront (browse products, add to cart, checkout end-to-end) and admin (login, manage products, view/fulfill/refund orders, confirm bank transfer). Verify all acceptance criteria across specs. Fix any bugs found (assign to owners). Then run the review meeting / feature showcase for the user.

Settle on one route-name scheme (storefront.cart / storefront.checkout / storefront.checkout.confirmation), add real routes to routes/storefront.php, update commerce component redirects + CheckoutComponentTest stubs to match, and remove the fragile runtime Route::get stubs from CheckoutComponentTest beforeEach. Full suite must be green.

Build Products\Show product page

Storefront\Products\Show Livewire component + view: image gallery, variant selector, price w/ compare-at, quantity, add-to-cart (dispatch add-to-cart to CartDrawer), stock messaging via catalog purchasable/inventory, breadcrumbs, description, tags. Route /products/{handle} name storefront.products.show.

Polish CartDrawer + Cart\Show views

Polish commerce's cart-drawer + cart/show blade views to spec: line items w/ thumbnails, quantity-selector component, remove, discount code input, totals, checkout button, empty state, ARIA dialog/focus. Reuse storefront components.

Polish commerce's checkout/show + checkout/confirmation views: multi-step stepper (Contact/Address, Shipping, Payment), order-summary sidebar, card form (4242), bank transfer instructions on confirmation. Align order-summary keys to checkout totals_json.

Build Search Modal + Index shells

Storefront\Search\Modal (listens open-search-modal, autocomplete UI shell) + Search\Index (results page shell reusing collection grid). Coordinate with platform for SearchService logic in #8 — provide UI + hooks only.

Build customer account pages

Account\Dashboard (recent orders + quick links), Account\Orders\Index (paginated), Account\Orders\Show (timeline, 404 on other customers' orders), Account\Addresses\Index (CRUD + set default). Enhance existing Login/Register to spec. Routes: /account, /account/orders, /account/orders/{orderNumber}, /account/addresses, POST logout.

Write tests + browser-verify full flow

tests/Feature/Customers/{CustomerAccountTest,AddressManagementTest} + storefront product/cart/checkout Livewire tests. Full suite green + pint. Browser-verify full purchase flow (browse->product->add->checkout->pay 4242->confirmation) + account pages at shop.test.

Foundation-level bug found by storefront: on /livewire/update requests, nested/global Livewire components (e.g. layout CartDrawer) 500 with "Target class [current_store] does not exist" because ResolveStore (registered as storefront/admin route-group middleware) does not re-run on the shared Livewire update endpoint. Fix: Livewire::addPersistentMiddleware(ResolveStore::class) + make ResolveStore self-detect storefront-vs-admin mode when invoked without the mode arg (persistent middleware drops args). Add a regression test exercising a nested component update path. Affects storefront (#6) + admin (#7). Owner: foundation.

Storefront-side polish for Phase 11. Owner: storefront. Move customer Login/Register from the Flux auth layout to the storefront-branded layout. Accessibility pass (skip links, ARIA, focus management in cart drawer + modals + checkout stepper). Verify dark mode + responsive (sm/md/lg/xl) across storefront pages; fill gaps. Confirm styled 404/503 render at shop.test. Browser-verify key pages. Full suite green + pint clean.


Create fulfillment submit button in modal


### Ran Playwright code
```js
await page.locator('[data-test="submit-fulfillment"]').click();
```
### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-37-52-276Z.yml)


### Page
- Page URL: http://shop.test/admin/orders/3
- Page Title: Admin
### Snapshot
```yaml
- generic [ref=e150]:
  - generic [ref=e151]:
    - generic [ref=e152]:
      - generic [ref=e153]:
        - heading "#1003" [level=1] [ref=e154]
        - generic [ref=e155]: Paid
        - generic [ref=e156]: Fulfilled
      - paragraph [ref=e157]: May 27, 2026 2:33 PM
      - button "Refund" [ref=e260]:
        - img [ref=e262]
        - img [ref=e265]
        - generic [ref=e267]: Refund
    - generic [ref=e176]:
      - generic [ref=e177]: Timeline
      - list [ref=e178]:
        - listitem [ref=e179]:
          - paragraph [ref=e181]: Order placed
          - paragraph [ref=e182]: May 27, 2026 2:33 PM
        - listitem [ref=e268]:
          - paragraph [ref=e270]: Payment received
          - paragraph [ref=e271]: May 31, 2026 2:33 PM
        - listitem [ref=e325]:
          - paragraph [ref=e327]: Fulfillment created
          - paragraph [ref=e328]: May 31, 2026 2:37 PM
    - generic [ref=e183]:
      - generic [ref=e184]: Order lines
      - table [ref=e187]:
        - rowgroup [ref=e188]:
          - row "Product Qty Unit Total" [ref=e189]:
            - columnheader "Product" [ref=e190]:
              - generic [ref=e191]: Product
            - columnheader "Qty" [ref=e192]:
              - generic [ref=e193]: Qty
            - columnheader "Unit" [ref=e194]:
              - generic [ref=e195]: Unit
            - columnheader "Total" [ref=e196]:
              - generic [ref=e197]: Total
        - rowgroup [ref=e198]:
          - row "Classic Cotton T-Shirt 2 29.00 USD 58.00 USD" [ref=e199]:
            - cell "Classic Cotton T-Shirt" [ref=e200]:
              - generic [ref=e201]: Classic Cotton T-Shirt
            - cell "2" [ref=e202]
            - cell "29.00 USD" [ref=e203]
            - cell "58.00 USD" [ref=e204]
          - row "Classic Cotton T-Shirt 2 30.00 USD 60.00 USD" [ref=e205]:
            - cell "Classic Cotton T-Shirt" [ref=e206]:
              - generic [ref=e207]: Classic Cotton T-Shirt
            - cell "2" [ref=e208]
            - cell "30.00 USD" [ref=e209]
            - cell "60.00 USD" [ref=e210]
      - generic [ref=e211]:
        - generic [ref=e212]:
          - generic [ref=e213]: Subtotal
          - generic [ref=e214]: 118.00 USD
        - generic [ref=e215]:
          - generic [ref=e216]: Shipping
          - generic [ref=e217]: 5.99 USD
        - generic [ref=e218]:
          - generic [ref=e219]: Tax
          - generic [ref=e220]: 9.44 USD
        - generic [ref=e221]:
          - generic [ref=e222]: Total
          - generic [ref=e223]: 133.43 USD
    - generic [ref=e224]:
      - generic [ref=e225]: Payment details
      - generic [ref=e226]:
        - generic [ref=e227]: Bank transfer
        - generic [ref=e228]: Captured
        - generic [ref=e229]: 133.43 USD
        - generic [ref=e230]: mock_5tnzt8kdxdivrt6f65lrgkhq
    - generic [ref=e329]:
      - generic [ref=e330]:
        - generic [ref=e331]:
          - generic [ref=e332]: "Fulfillment #4"
          - generic [ref=e333]: Pending
        - button "Mark as shipped" [ref=e335]:
          - img [ref=e337]
          - generic [ref=e340]: Mark as shipped
      - paragraph [ref=e341]: DHL
      - list [ref=e342]:
        - listitem [ref=e343]: Classic Cotton T-Shirt × 2
        - listitem [ref=e344]: Classic Cotton T-Shirt × 2
  - generic [ref=e238]:
    - generic [ref=e239]:
      - generic [ref=e240]: Customer
      - paragraph [ref=e241]: Liam Müller
      - paragraph [ref=e242]: liam.mueller@example.de
      - link "View customer" [ref=e243] [cursor=pointer]:
        - /url: http://shop.test/admin/customers/3
    - generic [ref=e244]:
      - generic [ref=e245]: Shipping address
      - generic [ref=e246]:
        - text: Liam Müller
        - text: Hauptstrasse 5
        - text: Berlin 10115
        - text: DE
    - generic [ref=e247]:
      - generic [ref=e248]: Billing address
      - generic [ref=e249]:
        - text: Liam Müller
        - text: Hauptstrasse 5
        - text: Berlin 10115
        - text: DE
```


**Fulfillment works end-to-end** — status → **Fulfilled**, "Fulfillment created" added to timeline, Fulfillment #4 card with DHL tracking + "Mark as shipped", **0 console errors** (the `OrderFulfilled` webhook handled gracefully too). The full order lifecycle (confirm payment → fulfill, with the guard) is verified.

Let me verify the Analytics page (admin's seeded data) and Products list.


### Ran Playwright code
```js
await page.goto('http://shop.test/admin/analytics');
```
### Page
- Page URL: http://shop.test/admin/analytics
- Page Title: Admin
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-38-09-525Z.yml)
### Events
- New console entries: .playwright-mcp/console-2026-05-31T14-38-09-360Z.log#L1


### Page
- Page URL: http://shop.test/admin/analytics
- Page Title: Admin
### Snapshot
```yaml
- generic [ref=e136]:
  - generic [ref=e137]:
    - generic [ref=e138]:
      - link [ref=e139] [cursor=pointer]:
        - /url: http://shop.test/admin
        - img [ref=e140]
      - img [ref=e142]
    - generic [ref=e145]: Analytics
  - generic [ref=e146]:
    - heading "Analytics" [level=1] [ref=e147]
    - combobox [ref=e149]:
      - option "Today"
      - option "Last 7 days"
      - option "Last 30 days" [selected]
      - option "Custom range"
  - generic [ref=e150]:
    - generic [ref=e151]:
      - paragraph [ref=e152]: Total Sales
      - generic [ref=e153]: 112,097.22 USD
    - generic [ref=e154]:
      - paragraph [ref=e155]: Orders
      - generic [ref=e156]: 1,224
    - generic [ref=e157]:
      - paragraph [ref=e158]: Avg Order Value
      - generic [ref=e159]: 91.58 USD
    - generic [ref=e160]:
      - paragraph [ref=e161]: Conversion Rate
      - generic [ref=e162]: 11.50%
  - generic [ref=e164]: Revenue over time
  - generic [ref=e166]:
    - generic [ref=e167]: Conversion funnel
    - generic [ref=e168]:
      - generic [ref=e169]:
        - generic [ref=e170]: Visits
        - generic [ref=e173]: 10,648
      - generic [ref=e174]:
        - generic [ref=e175]: Add to Cart
        - generic [ref=e178]: 3,497
      - generic [ref=e179]:
        - generic [ref=e180]: Checkout Started
        - generic [ref=e183]: 1,867
      - generic [ref=e184]:
        - generic [ref=e185]: Checkout Completed
        - generic [ref=e188]: 1,224
```


✅ **Analytics page now fully populated** (admin's fix) — Total Sales 112,097.22 USD, 1,224 orders, AOV 91.58 USD, **Conversion 11.50%**, and a proper narrowing funnel (Visits 10,648 → Add-to-Cart 3,497 → Checkout Started 1,867 → Completed 1,224). Let me verify the Products list and customer account, then the 404 page.


### Ran Playwright code
```js
await page.goto('http://shop.test/admin/products');
```
### Page
- Page URL: http://shop.test/admin/products
- Page Title: Admin
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-38-22-044Z.yml)
### Events
- New console entries: .playwright-mcp/console-2026-05-31T14-38-21-835Z.log#L1


### Result
- [Screenshot of viewport](./verify-admin-products.jpeg)
### Ran Playwright code
```js
// Screenshot viewport and save it as ./verify-admin-products.jpeg
await page.screenshot({
  path: './verify-admin-products.jpeg',
  quality: 90,
  scale: 'css',
  type: 'jpeg'
});
```


The Products list page shows the chrome but the **main content area appears empty** in the screenshot. This could be a transient render or a real issue — let me get the accessibility snapshot to see the actual DOM.


### Page
- Page URL: http://shop.test/admin/products
- Page Title: Admin
### Snapshot
```yaml
- generic [ref=e136]:
  - generic [ref=e137]:
    - generic [ref=e138]:
      - link [ref=e139] [cursor=pointer]:
        - /url: http://shop.test/admin
        - img [ref=e140]
      - img [ref=e142]
    - generic [ref=e145]: Products
  - generic [ref=e146]:
    - heading "Products" [level=1] [ref=e147]
    - link "Add product" [ref=e148] [cursor=pointer]:
      - /url: http://shop.test/admin/products/create
      - img [ref=e149]
      - generic [ref=e151]: Add product
  - generic [ref=e152]:
    - generic [ref=e153]:
      - generic:
        - img
      - textbox "Search products..." [ref=e154]
    - combobox [ref=e156]:
      - option "All statuses" [selected]
      - option "Draft"
      - option "Active"
      - option "Archived"
    - combobox [ref=e157]:
      - option "All types" [selected]
      - option "Accessories"
      - option "Apparel"
      - option "Footwear"
      - option "Home"
  - generic [ref=e159]:
    - table [ref=e161]:
      - rowgroup [ref=e162]:
        - row "Select all Image Title Status Variants Type Vendor Updated" [ref=e163]:
          - columnheader "Select all" [ref=e164]:
            - checkbox "Select all" [ref=e166]
          - columnheader "Image" [ref=e168]:
            - generic [ref=e169]: Image
          - columnheader "Title" [ref=e170]:
            - button "Title" [ref=e172]:
              - generic [ref=e173]: Title
              - img [ref=e176]
          - columnheader "Status" [ref=e178]:
            - generic [ref=e179]: Status
          - columnheader "Variants" [ref=e180]:
            - generic [ref=e181]: Variants
          - columnheader "Type" [ref=e182]:
            - generic [ref=e183]: Type
          - columnheader "Vendor" [ref=e184]:
            - generic [ref=e185]: Vendor
          - columnheader "Updated" [ref=e186]:
            - button "Updated" [ref=e188]:
              - generic [ref=e189]: Updated
              - img [ref=e191]
      - rowgroup [ref=e193]:
        - row "Select product Merino Wool Beanie Merino Wool Beanie Active 3 Accessories Northbound 4 minutes ago" [ref=e194]:
          - cell "Select product" [ref=e195]:
            - checkbox "Select product" [ref=e196]
          - cell "Merino Wool Beanie" [ref=e198]:
            - img "Merino Wool Beanie" [ref=e199]
          - cell "Merino Wool Beanie" [ref=e200]:
            - link "Merino Wool Beanie" [ref=e201] [cursor=pointer]:
              - /url: http://shop.test/admin/products/4/edit
          - cell "Active" [ref=e202]:
            - generic [ref=e203]: Active
          - cell "3" [ref=e204]
          - cell "Accessories" [ref=e205]
          - cell "Northbound" [ref=e206]
          - cell "4 minutes ago" [ref=e207]
        - row "Select product Canvas Tote Bag Canvas Tote Bag Active 1 Accessories Acme Basics 4 minutes ago" [ref=e208]:
          - cell "Select product" [ref=e209]:
            - checkbox "Select product" [ref=e210]
          - cell "Canvas Tote Bag" [ref=e212]:
            - img "Canvas Tote Bag" [ref=e213]
          - cell "Canvas Tote Bag" [ref=e214]:
            - link "Canvas Tote Bag" [ref=e215] [cursor=pointer]:
              - /url: http://shop.test/admin/products/5/edit
          - cell "Active" [ref=e216]:
            - generic [ref=e217]: Active
          - cell "1" [ref=e218]
          - cell "Accessories" [ref=e219]
          - cell "Acme Basics" [ref=e220]
          - cell "4 minutes ago" [ref=e221]
        - row "Select product Leather Card Holder Leather Card Holder Active 2 Accessories Atelier 4 minutes ago" [ref=e222]:
          - cell "Select product" [ref=e223]:
            - checkbox "Select product" [ref=e224]
          - cell "Leather Card Holder" [ref=e226]:
            - img "Leather Card Holder" [ref=e227]
          - cell "Leather Card Holder" [ref=e228]:
            - link "Leather Card Holder" [ref=e229] [cursor=pointer]:
              - /url: http://shop.test/admin/products/6/edit
          - cell "Active" [ref=e230]:
            - generic [ref=e231]: Active
          - cell "2" [ref=e232]
          - cell "Accessories" [ref=e233]
          - cell "Atelier" [ref=e234]
          - cell "4 minutes ago" [ref=e235]
        - row "Select product Polarized Sunglasses Polarized Sunglasses Active 2 Accessories Velocity 4 minutes ago" [ref=e236]:
          - cell "Select product" [ref=e237]:
            - checkbox "Select product" [ref=e238]
          - cell "Polarized Sunglasses" [ref=e240]:
            - img "Polarized Sunglasses" [ref=e241]
          - cell "Polarized Sunglasses" [ref=e242]:
            - link "Polarized Sunglasses" [ref=e243] [cursor=pointer]:
              - /url: http://shop.test/admin/products/13/edit
          - cell "Active" [ref=e244]:
            - generic [ref=e245]: Active
          - cell "2" [ref=e246]
          - cell "Accessories" [ref=e247]
          - cell "Velocity" [ref=e248]
          - cell "4 minutes ago" [ref=e249]
        - row "Select product Travel Backpack 25L Travel Backpack 25L Active 2 Accessories Velocity 4 minutes ago" [ref=e250]:
          - cell "Select product" [ref=e251]:
            - checkbox "Select product" [ref=e252]
          - cell "Travel Backpack 25L" [ref=e254]:
            - img "Travel Backpack 25L" [ref=e255]
          - cell "Travel Backpack 25L" [ref=e256]:
            - link "Travel Backpack 25L" [ref=e257] [cursor=pointer]:
              - /url: http://shop.test/admin/products/17/edit
          - cell "Active" [ref=e258]:
            - generic [ref=e259]: Active
          - cell "2" [ref=e260]
          - cell "Accessories" [ref=e261]
          - cell "Velocity" [ref=e262]
          - cell "4 minutes ago" [ref=e263]
        - row "Select product Cashmere Scarf Cashmere Scarf Active 3 Accessories Atelier 4 minutes ago" [ref=e264]:
          - cell "Select product" [ref=e265]:
            - checkbox "Select product" [ref=e266]
          - cell "Cashmere Scarf" [ref=e268]:
            - img "Cashmere Scarf" [ref=e269]
          - cell "Cashmere Scarf" [ref=e270]:
            - link "Cashmere Scarf" [ref=e271] [cursor=pointer]:
              - /url: http://shop.test/admin/products/18/edit
          - cell "Active" [ref=e272]:
            - generic [ref=e273]: Active
          - cell "3" [ref=e274]
          - cell "Accessories" [ref=e275]
          - cell "Atelier" [ref=e276]
          - cell "4 minutes ago" [ref=e277]
        - row "Select product Classic Cotton T-Shirt Classic Cotton T-Shirt Active 12 Apparel Acme Basics 4 minutes ago" [ref=e278]:
          - cell "Select product" [ref=e279]:
            - checkbox "Select product" [ref=e280]
          - cell "Classic Cotton T-Shirt" [ref=e282]:
            - img "Classic Cotton T-Shirt" [ref=e283]
          - cell "Classic Cotton T-Shirt" [ref=e284]:
            - link "Classic Cotton T-Shirt" [ref=e285] [cursor=pointer]:
              - /url: http://shop.test/admin/products/1/edit
          - cell "Active" [ref=e286]:
            - generic [ref=e287]: Active
          - cell "12" [ref=e288]
          - cell "Apparel" [ref=e289]
          - cell "Acme Basics" [ref=e290]
          - cell "4 minutes ago" [ref=e291]
        - row "Select product Organic Cotton Hoodie Organic Cotton Hoodie Active 6 Apparel Acme Basics 4 minutes ago" [ref=e292]:
          - cell "Select product" [ref=e293]:
            - checkbox "Select product" [ref=e294]
          - cell "Organic Cotton Hoodie" [ref=e296]:
            - img "Organic Cotton Hoodie" [ref=e297]
          - cell "Organic Cotton Hoodie" [ref=e298]:
            - link "Organic Cotton Hoodie" [ref=e299] [cursor=pointer]:
              - /url: http://shop.test/admin/products/2/edit
          - cell "Active" [ref=e300]:
            - generic [ref=e301]: Active
          - cell "6" [ref=e302]
          - cell "Apparel" [ref=e303]
          - cell "Acme Basics" [ref=e304]
          - cell "4 minutes ago" [ref=e305]
        - row "Select product Slim Fit Chinos Slim Fit Chinos Active 4 Apparel Northbound 4 minutes ago" [ref=e306]:
          - cell "Select product" [ref=e307]:
            - checkbox "Select product" [ref=e308]
          - cell "Slim Fit Chinos" [ref=e310]:
            - img "Slim Fit Chinos" [ref=e311]
          - cell "Slim Fit Chinos" [ref=e312]:
            - link "Slim Fit Chinos" [ref=e313] [cursor=pointer]:
              - /url: http://shop.test/admin/products/3/edit
          - cell "Active" [ref=e314]:
            - generic [ref=e315]: Active
          - cell "4" [ref=e316]
          - cell "Apparel" [ref=e317]
          - cell "Northbound" [ref=e318]
          - cell "4 minutes ago" [ref=e319]
        - row "Select product Linen Button-Down Shirt Linen Button-Down Shirt Active 8 Apparel Northbound 4 minutes ago" [ref=e320]:
          - cell "Select product" [ref=e321]:
            - checkbox "Select product" [ref=e322]
          - cell "Linen Button-Down Shirt" [ref=e324]:
            - img "Linen Button-Down Shirt" [ref=e325]
          - cell "Linen Button-Down Shirt" [ref=e326]:
            - link "Linen Button-Down Shirt" [ref=e327] [cursor=pointer]:
              - /url: http://shop.test/admin/products/9/edit
          - cell "Active" [ref=e328]:
            - generic [ref=e329]: Active
          - cell "8" [ref=e330]
          - cell "Apparel" [ref=e331]
          - cell "Northbound" [ref=e332]
          - cell "4 minutes ago" [ref=e333]
        - row "Select product Performance Socks 3-Pack Performance Socks 3-Pack Active 2 Apparel Velocity 4 minutes ago" [ref=e334]:
          - cell "Select product" [ref=e335]:
            - checkbox "Select product" [ref=e336]
          - cell "Performance Socks 3-Pack" [ref=e338]:
            - img "Performance Socks 3-Pack" [ref=e339]
          - cell "Performance Socks 3-Pack" [ref=e340]:
            - link "Performance Socks 3-Pack" [ref=e341] [cursor=pointer]:
              - /url: http://shop.test/admin/products/14/edit
          - cell "Active" [ref=e342]:
            - generic [ref=e343]: Active
          - cell "2" [ref=e344]
          - cell "Apparel" [ref=e345]
          - cell "Velocity" [ref=e346]
          - cell "4 minutes ago" [ref=e347]
        - row "Select product Quilted Field Jacket Quilted Field Jacket Active 4 Apparel Northbound 4 minutes ago" [ref=e348]:
          - cell "Select product" [ref=e349]:
            - checkbox "Select product" [ref=e350]
          - cell "Quilted Field Jacket" [ref=e352]:
            - img "Quilted Field Jacket" [ref=e353]
          - cell "Quilted Field Jacket" [ref=e354]:
            - link "Quilted Field Jacket" [ref=e355] [cursor=pointer]:
              - /url: http://shop.test/admin/products/15/edit
          - cell "Active" [ref=e356]:
            - generic [ref=e357]: Active
          - cell "4" [ref=e358]
          - cell "Apparel" [ref=e359]
          - cell "Northbound" [ref=e360]
          - cell "4 minutes ago" [ref=e361]
        - row "Select product Denim Jacket Denim Jacket Active 4 Apparel Northbound 4 minutes ago" [ref=e362]:
          - cell "Select product" [ref=e363]:
            - checkbox "Select product" [ref=e364]
          - cell "Denim Jacket" [ref=e366]:
            - img "Denim Jacket" [ref=e367]
          - cell "Denim Jacket" [ref=e368]:
            - link "Denim Jacket" [ref=e369] [cursor=pointer]:
              - /url: http://shop.test/admin/products/19/edit
          - cell "Active" [ref=e370]:
            - generic [ref=e371]: Active
          - cell "4" [ref=e372]
          - cell "Apparel" [ref=e373]
          - cell "Northbound" [ref=e374]
          - cell "4 minutes ago" [ref=e375]
        - row "Select product Running Sneakers Running Sneakers Active 5 Footwear Velocity 4 minutes ago" [ref=e376]:
          - cell "Select product" [ref=e377]:
            - checkbox "Select product" [ref=e378]
          - cell "Running Sneakers" [ref=e380]:
            - img "Running Sneakers" [ref=e381]
          - cell "Running Sneakers" [ref=e382]:
            - link "Running Sneakers" [ref=e383] [cursor=pointer]:
              - /url: http://shop.test/admin/products/7/edit
          - cell "Active" [ref=e384]:
            - generic [ref=e385]: Active
          - cell "5" [ref=e386]
          - cell "Footwear" [ref=e387]
          - cell "Velocity" [ref=e388]
          - cell "4 minutes ago" [ref=e389]
        - row "Select product Suede Chelsea Boots Suede Chelsea Boots Active 4 Footwear Atelier 4 minutes ago" [ref=e390]:
          - cell "Select product" [ref=e391]:
            - checkbox "Select product" [ref=e392]
          - cell "Suede Chelsea Boots" [ref=e394]:
            - img "Suede Chelsea Boots" [ref=e395]
          - cell "Suede Chelsea Boots" [ref=e396]:
            - link "Suede Chelsea Boots" [ref=e397] [cursor=pointer]:
              - /url: http://shop.test/admin/products/8/edit
          - cell "Active" [ref=e398]:
            - generic [ref=e399]: Active
          - cell "4" [ref=e400]
          - cell "Footwear" [ref=e401]
          - cell "Atelier" [ref=e402]
          - cell "4 minutes ago" [ref=e403]
    - generic [ref=e404]:
      - generic [ref=e405]: Showing 1 to 15 of 20 results
      - generic [ref=e406]:
        - generic "« Previous" [ref=e407]:
          - img [ref=e408]
        - generic [ref=e410]: "1"
        - button "2" [ref=e411]
        - button "Next »" [ref=e412]:
          - img [ref=e413]
```


✅ **Products list fully renders** (the earlier blank screenshot was a transient paint/scroll artifact) — 15 of 20 products paginated, with images, status badges, variant counts, type/vendor, search + status/type filters, "Add product", and working pagination. No bug.

Admin panel is thoroughly verified. Let me verify the **customer account** flow (storefront) and the 404 page, then I'll have everything for the review.


### Ran Playwright code
```js
await page.goto('http://shop.test/account/login');
```
### Page
- Page URL: http://shop.test/account/login
- Page Title: Acme Fashion
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-39-04-171Z.yml)
### Events
- New console entries: .playwright-mcp/console-2026-05-31T14-39-04-017Z.log#L1


### Page
- Page URL: http://shop.test/account/login
- Page Title: Acme Fashion
### Snapshot
```yaml
- main [ref=e31]:
  - generic [ref=e32]:
    - generic [ref=e33]:
      - heading "Log in to your account" [level=1] [ref=e34]
      - paragraph [ref=e35]: Enter your email and password below to log in
    - generic [ref=e36]:
      - generic [ref=e37]:
        - generic [ref=e38]: Email address
        - textbox "Email address" [active] [ref=e40]:
          - /placeholder: email@example.com
      - generic [ref=e41]:
        - generic [ref=e42]: Password
        - generic [ref=e43]:
          - textbox "Password" [ref=e44]
          - button "Toggle password visibility" [ref=e46]:
            - img [ref=e47]
      - generic [ref=e50]:
        - checkbox "Remember me" [ref=e51]
        - generic [ref=e53]: Remember me
      - button "Log in" [ref=e54]:
        - img [ref=e56]
        - generic [ref=e59]: Log in
    - generic [ref=e60]:
      - text: Don't have an account?
      - link "Create one" [ref=e61] [cursor=pointer]:
        - /url: http://shop.test/account/register
```


Branded customer login renders (storefront's #19 work). Let me log in as the demo customer and verify the account dashboard.


### Ran Playwright code
```js
await page.getByRole('textbox', { name: 'Email address' }).fill('customer@shop.test');
await page.getByRole('textbox', { name: 'Password' }).fill('password');
```


### Ran Playwright code
```js
await page.getByRole('button', { name: 'Log in' }).click();
```
### Page
- Page URL: http://shop.test/account
- Page Title: Account - Acme Fashion
### Snapshot
- [Snapshot](.playwright-mcp/page-2026-05-31T14-39-19-471Z.yml)


Customer login succeeded → redirected to `/account`. Let me view the dashboard + verify order history is scoped to this customer.


### Result
Total messages: 3 (Errors: 0, Warnings: 0)
Returning 0 messages for level "error"


Error: ### Error
Error: Ref e31 not found in the current page snapshot. Try capturing new snapshot.