app/Actions:
Fortify

app/Concerns:
Livewire
PasswordValidationRules.php
Policies
ProfileValidationRules.php

app/Http:
Controllers
Requests

app/Livewire:
Actions
Admin
Settings
Storefront

app/Observers:

app/Services:
Catalog

app/Support:
Money
---routes---
console.php
settings.php
web.php
---tests---
tests/Pest.php
tests/TestCase.php
tests/Unit/ExampleTest.php
tests/Feature/ExampleTest.php
tests/Feature/DashboardTest.php
tests/Feature/Settings/TwoFactorAuthenticationTest.php
tests/Feature/Settings/PasswordUpdateTest.php
tests/Feature/Settings/ProfileUpdateTest.php
tests/Feature/Auth/PasswordConfirmationTest.php
tests/Feature/Auth/EmailVerificationTest.php
tests/Feature/Auth/TwoFactorChallengeTest.php
tests/Feature/Auth/RegistrationTest.php
tests/Feature/Auth/AuthenticationTest.php
tests/Feature/Auth/PasswordResetTest.php
---migrations---
0001_01_01_000000_create_users_table.php
0001_01_01_000001_create_cache_table.php
0001_01_01_000002_create_jobs_table.php
2025_08_14_170933_add_two_factor_columns_to_users_table.php
---views---
components
dashboard.blade.php
flux
layouts
livewire
partials
storefront
welcome.blade.php
---composer scripts---
Array
(
    [setup] => Array
        (
            [0] => composer install
            [1] => @php -r "file_exists('.env') || copy('.env.example', '.env');"
            [2] => @php artisan key:generate
            [3] => @php artisan migrate --force
            [4] => npm install
            [5] => npm run build
        )

    [dev] => Array
        (
            [0] => Composer\Config::disableProcessTimeout
            [1] => npx concurrently -c "#93c5fd,#c4b5fd,#fb7185,#fdba74" "php artisan serve" "php artisan queue:listen --tries=1 --timeout=0" "php artisan pail --timeout=0" "npm run dev" --names=server,queue,logs,vite --kill-others
        )

    [lint] => Array
        (
            [0] => pint --parallel
        )

    [test:lint] => Array
        (
            [0] => pint --parallel --test
        )

    [test] => Array
        (
            [0] => @php artisan config:clear --ansi
            [1] => @test:lint
            [2] => @php artisan test
        )

    [post-autoload-dump] => Array
        (
            [0] => Illuminate\Foundation\ComposerScripts::postAutoloadDump
            [1] => @php artisan package:discover --ansi
        )

    [post-update-cmd] => Array
        (
            [0] => @php artisan vendor:publish --tag=laravel-assets --ansi --force
        )

    [post-root-package-install] => Array
        (
            [0] => @php -r "file_exists('.env') || copy('.env.example', '.env');"
        )

    [post-create-project-cmd] => Array
        (
            [0] => @php artisan key:generate --ansi
            [1] => @php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"
            [2] => @php artisan migrate --graceful --ansi
        )

    [pre-package-uninstall] => Array
        (
            [0] => Illuminate\Foundation\ComposerScripts::prePackageUninstall
        )

)


Your mission is to implement an entire shop system based on the specifications im specs/*. You must do in one go without stopping. You might use sub-agents or team mode! You must test everything via P…


Implement complete shop system with tests and review


Bash

find app resources/views/storefront resources/views/livewire -type f | head -100; echo "===routes/web.php==="; cat routes/web.php | head -80

List all app files and web routes


app/Providers/AppServiceProvider.php
app/Providers/FortifyServiceProvider.php
app/Models/User.php
app/Concerns/ProfileValidationRules.php
app/Concerns/PasswordValidationRules.php
resources/views/livewire/settings/delete-user-form.blade.php
resources/views/livewire/settings/two-factor.blade.php
resources/views/livewire/settings/password.blade.php
resources/views/livewire/settings/appearance.blade.php
resources/views/livewire/settings/profile.blade.php
resources/views/livewire/auth/reset-password.blade.php
resources/views/livewire/auth/register.blade.php
resources/views/livewire/auth/two-factor-challenge.blade.php
resources/views/livewire/auth/confirm-password.blade.php
resources/views/livewire/auth/forgot-password.blade.php
resources/views/livewire/auth/verify-email.blade.php
resources/views/livewire/auth/login.blade.php
app/Livewire/Settings/TwoFactor.php
app/Livewire/Settings/DeleteUserForm.php
app/Livewire/Settings/Password.php
app/Livewire/Settings/Profile.php
app/Livewire/Settings/Appearance.php
app/Livewire/Actions/Logout.php
app/Http/Controllers/Controller.php
app/Actions/Fortify/ResetUserPassword.php
app/Actions/Fortify/CreateNewUser.php
resources/views/livewire/settings/two-factor/recovery-codes.blade.php
app/Livewire/Settings/TwoFactor/RecoveryCodes.php
===routes/web.php===
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
})->name('home');

Route::view('dashboard', 'dashboard')
    ->middleware(['auth', 'verified'])
    ->name('dashboard');

require __DIR__.'/settings.php';


1562	| manages store domains | add a new domain | store_domain created | happy path |
1563
1564	---
1565
1566	#### tests/Feature/Search/SearchTest.php
1567
1568	| Test Name | Setup / Preconditions | Key Assertions | Category |
1569	|-----------|----------------------|----------------|----------|
1570	| returns products matching search query | products: "Blue Cotton T-Shirt", "Red Wool Sweater"; search "cotton" | finds "Blue Cotton T-Shirt" | happy path |
1571	| scopes search to current store | store A has "T-Shirt", store B has "T-Shirt Deluxe"; search "t-shirt" on store A | only store A product returned | authorization |
1572	| returns empty for no matches | search "xyznonexistent" | empty results | edge case |
1573	| logs search query for analytics | perform search | search_queries row created | happy path |
1574	| paginates search results | 25 matching products, 12 per page | page 1 has 12, page 2 has 12, page 3 has 1 | happy path |
1575
1576	---
1577
1578	#### tests/Feature/Search/AutocompleteTest.php
1579
1580	| Test Name | Setup / Preconditions | Key Assertions | Category |
1581	|-----------|----------------------|----------------|----------|
1582	| returns suggestions matching prefix | products: "Summer Dress", "Summer Hat", "Winter Coat"; autocomplete prefix "sum" | "Summer Dress" and "Summer Hat" returned | happy path |
1583	| limits results to configured count | 20 matching products, limit 5 | 5 results returned | happy path |
1584	| returns empty for very short prefix | autocomplete prefix "a" (below minimum) | empty or all results | edge case |
1585
1586	---
1587
1588	#### tests/Feature/Analytics/EventIngestionTest.php
1589
1590	| Test Name | Setup / Preconditions | Key Assertions | Category |
1591	|-----------|----------------------|----------------|----------|
1592	| tracks a page view event | POST analytics event: type=page_view | analytics_events row created | happy path |
1593	| tracks an add to cart event | POST event: type=add_to_cart, properties include product_id | event stored with properties | happy path |
1594	| scopes events to current store | track event on store A | event.store_id = store A id | happy path |
1595	| includes session ID when available | track event with session | session_id stored | happy path |
1596	| includes customer ID when authenticated | track event as logged-in customer | customer_id stored | happy path |
1597
1598	---
1599
1600	#### tests/Feature/Analytics/AggregationTest.php
1601
1602	| Test Name | Setup / Preconditions | Key Assertions | Category |
1603	|-----------|----------------------|----------------|----------|
1604	| aggregates daily metrics from raw events | create events: 5 page_views, 3 add_to_carts, 2 checkout_completed; run AggregateAnalytics job | analytics_daily row with correct counts | happy path |
1605	| calculates revenue and AOV correctly | 3 orders: 1000, 2000, 3000 cents; aggregate | revenue=6000, aov=2000 | happy path |
1606	| runs idempotently | run aggregation twice for same date | analytics_daily row has correct values (not doubled) | edge case |
1607
1608	---
1609
1610	#### tests/Feature/Webhooks/WebhookDeliveryTest.php
1611
1612	| Test Name | Setup / Preconditions | Key Assertions | Category |
1613	|-----------|----------------------|----------------|----------|
1614	| delivers a webhook to a subscribed URL | mock HTTP client; subscription for event_type "order.created"; dispatch event | HTTP POST made to target_url | happy path |
1615	| signs the payload with HMAC | deliver webhook | `X-Platform-Signature` header present with valid signature | happy path |
1616	| retries failed deliveries with exponential backoff | first delivery returns 500 | job retried, attempt_count incremented | edge case |
1617	| marks delivery as failed after max retries | 6 total attempts exhausted (1 initial + 5 retries) | delivery status = failed | edge case |
1618	| pauses subscription after circuit breaker threshold | 5 consecutive failures | subscription status = paused | edge case |
1619
1620	---
1621
1622	#### tests/Feature/Webhooks/WebhookSignatureTest.php
1623
1624	| Test Name | Setup / Preconditions | Key Assertions | Category |
1625	|-----------|----------------------|----------------|----------|
1626	| generates a valid HMAC-SHA256 signature | payload: '{"event":"order.created"}'; secret: 'test-secret' | signature matches expected hash | happy path |
1627	| verifies a valid signature | generate signature, then verify | returns true | happy path |
1628	| rejects a tampered payload | sign original, verify with modified payload | returns false | validation |
1629	| rejects an incorrect secret | sign with secret A, verify with secret B | returns false | validation |
1630
1631	---
1632
1633	#### tests/Feature/Api/StorefrontCartApiTest.php
1634
1635	| Test Name | Setup / Preconditions | Key Assertions | Category |
1636	|-----------|----------------------|----------------|----------|
1637	| creates a cart | POST /api/storefront/v1/carts | 201 response, body has cart id, version 1 | happy path |
1638	| retrieves a cart with lines and totals | create cart with 2 lines; GET /api/storefront/v1/carts/{id} | 200 response, lines array has 2 items, totals calculated | happy path |
1639	| adds a line to the cart | POST /api/storefront/v1/carts/{id}/lines | 200 response, line added | happy path |
1640	| updates a line quantity | PUT /api/storefront/v1/carts/{id}/lines/{lineId} | 200 response, quantity updated | happy path |
1641	| removes a line | DELETE /api/storefront/v1/carts/{id}/lines/{lineId} | 200 response, line removed | happy path |
1642	| validates variant exists on add | add nonexistent variant_id | 422 response | validation |
1643	| validates quantity is positive | add with quantity=0 | 422 response | validation |
1644	| returns 404 for nonexistent cart | GET /api/storefront/v1/carts/999 | 404 response | validation |
1645
1646	---
1647
1648	#### tests/Feature/Api/StorefrontCheckoutApiTest.php
1649
1650	| Test Name | Setup / Preconditions | Key Assertions | Category |
1651	|-----------|----------------------|----------------|----------|
1652	| creates a checkout from a cart | POST /api/storefront/v1/checkouts with cart_id | 201 response, checkout status=started | happy path |
1653	| sets checkout address | PUT /api/storefront/v1/checkouts/{id}/address | 200 response, status=addressed | happy path |
1654	| selects a shipping method | PUT /api/storefront/v1/checkouts/{id}/shipping-method | 200 response, status=shipping_selected, available rates returned in response | happy path |
1655	| applies a discount code | POST /api/storefront/v1/checkouts/{id}/apply-discount with code | 200 response, discount applied in totals | happy path |
1656	| retrieves checkout with totals | GET /api/storefront/v1/checkouts/{id} | 200 response, includes totals_json fields | happy path |
1657	| selects a payment method | PUT /api/storefront/v1/checkouts/{id}/payment-method with method=credit_card | 200 response, status=payment_selected, payment_method stored | happy path |
1658	| completes checkout with credit card payment | POST /api/storefront/v1/checkouts/{id}/pay with card fields | 200 response, status=completed, order created | happy path |
1659	| rejects payment with declined card | POST /api/storefront/v1/checkouts/{id}/pay with magic decline number | 422 response, payment declined message | validation |
1660	| validates required address fields | PUT address with missing city | 422 response | validation |
1661
1662	---
1663
1664	#### tests/Feature/Api/AdminProductApiTest.php
1665
1666	| Test Name | Setup / Preconditions | Key Assertions | Category |
1667	|-----------|----------------------|----------------|----------|
1668	| lists products with authentication | GET /api/admin/v1/stores/{storeId}/products with Bearer token | 200 response, paginated product list | happy path |
1669	| creates a product via API | POST /api/admin/v1/stores/{storeId}/products with product data | 201 response, product in response | happy path |
1670	| updates a product via API | PUT /api/admin/v1/stores/{storeId}/products/{id} with updated data | 200 response, product updated | happy path |
1671	| deletes a draft product via API | DELETE /api/admin/v1/stores/{storeId}/products/{id} | 200 response | happy path |
1672	| requires write-products ability for mutations | token with only read-products; POST /api/admin/v1/stores/{storeId}/products | 403 response | authorization |
1673	| returns 401 without token | GET /api/admin/v1/stores/{storeId}/products without auth header | 401 response | authorization |
1674	| paginates results | 25 products, default page size 15 | first page has 15 items, meta has total=25 | happy path |
1675
1676	---
1677
1678	#### tests/Feature/Api/AdminOrderApiTest.php
1679
1680	| Test Name | Setup / Preconditions | Key Assertions | Category |
1681	|-----------|----------------------|----------------|----------|
1682	| lists orders with authentication | GET /api/admin/v1/stores/{storeId}/orders with Bearer token | 200 response, order list | happy path |
1683	| retrieves a single order | GET /api/admin/v1/stores/{storeId}/orders/{id} | 200 response, order with lines, payments, fulfillments | happy path |
1684	| filters orders by status | GET /api/admin/v1/stores/{storeId}/orders?status=paid | only paid orders returned | happy path |
1685	| creates a fulfillment via API | POST /api/admin/v1/stores/{storeId}/orders/{id}/fulfillments | 201 response, fulfillment created | happy path |
1686	| creates a refund via API | POST /api/admin/v1/stores/{storeId}/orders/{id}/refunds | 201 response, refund created | happy path |
1687	| requires write-orders ability for mutations | token with only read-orders; POST fulfillment | 403 response | authorization |
1688
1689	---
1690
1691	## 4. Files to Create Inventory
1692
1693	### Models (with migration, factory, seeder)
1694
1695	| Name | Purpose |
1696	|------|---------|
1697	| `Organization` | Top-level tenant entity |
1698	| `Store` | Individual store within an organization |
1699	| `StoreDomain` | Domain mapping for a store |
1700	| `StoreUser` | Pivot model with role attribute |
1701	| `StoreSettings` | Per-store configuration |
1702	| `Product` | Product entity |
1703	| `ProductOption` | Product option definition |
1704	| `ProductOptionValue` | Option value entry |
1705	| `ProductVariant` | Product variant (SKU-level) |
1706	| `InventoryItem` | Inventory tracking per variant |
1707	| `Collection` | Product collection/category |
1708	| `ProductMedia` | Product images and videos |
1709	| `Theme` | Store theme entity |
1710	| `ThemeFile` | Individual theme file |
1711	| `ThemeSettings` | Theme configuration |
1712	| `Page` | CMS page |
1713	| `NavigationMenu` | Navigation menu entity |
1714	| `NavigationItem` | Individual navigation item |
1715	| `Cart` | Shopping cart entity |
1716	| `CartLine` | Individual cart line item |
1717	| `Checkout` | Checkout session entity |
1718	| `ShippingZone` | Shipping zone configuration |
1719	| `ShippingRate` | Shipping rate within a zone |
1720	| `TaxSettings` | Per-store tax configuration |
1721	| `Discount` | Discount code or automatic discount |
1722	| `Customer` | Store customer entity |
1723	| `CustomerAddress` | Customer address |
1724	| `Order` | Order entity |
1725	| `OrderLine` | Order line item |
1726	| `Payment` | Payment record |
1727	| `Refund` | Refund record |
1728	| `Fulfillment` | Fulfillment record |
1729	| `FulfillmentLine` | Fulfillment line item |
1730	| `App` | Third-party app entity |
1731	| `AppInstallation` | App installation per store |
1732	| `OauthClient` | OAuth client for app |
1733	| `OauthToken` | OAuth token for installation |
1734	| `WebhookSubscription` | Webhook subscription per store |
1735	| `WebhookDelivery` | Webhook delivery log |
1736	| `AnalyticsEvent` | Raw analytics event |
1737	| `AnalyticsDaily` | Pre-aggregated daily analytics |
1738	| `SearchSettings` | Per-store search configuration |
1739	| `SearchQuery` | Search query log |
1740
1741	### Livewire Components - Admin
1742
1743	| Name | Purpose |
1744	|------|---------|
1745	| `Admin/Auth/Login` | Admin login form |
1746	| `Admin/Dashboard` | Admin dashboard |
1747	| `Admin/Layout/Sidebar` | Admin sidebar navigation |
1748	| `Admin/Layout/TopBar` | Admin top bar with store selector |
1749	| `Admin/Products/Index` | Product list |
1750	| `Admin/Products/Form` | Shared product form (create/edit mode) |
1751	| `Admin/Collections/Index` | Collection list |
1752	| `Admin/Collections/Form` | Shared collection form (create/edit mode) |
1753	| `Admin/Orders/Index` | Order list |
1754	| `Admin/Orders/Show` | Order detail |
1755	| `Admin/Customers/Index` | Customer list |
1756	| `Admin/Customers/Show` | Customer detail |
1757	| `Admin/Discounts/Index` | Discount list |
1758	| `Admin/Discounts/Form` | Shared discount form (create/edit mode) |
1759	| `Admin/Settings/Index` | Settings tabs |
1760	| `Admin/Settings/Shipping` | Shipping zone/rate management |
1761	| `Admin/Settings/Taxes` | Tax settings |
1762	| `Admin/Inventory/Index` | Inventory management - list and filter inventory items, adjust quantities (Spec 03 Section 8) |
1763	| `Admin/Themes/Index` | Theme list |
1764	| `Admin/Themes/Editor` | Theme editor |
1765	| `Admin/Pages/Index` | Page list |
1766	| `Admin/Pages/Form` | Shared page form (create/edit mode) |
1767	| `Admin/Navigation/Index` | Navigation menu management |
1768	| `Admin/Analytics/Index` | Analytics dashboard |
1769	| `Admin/Search/Settings` | Search settings |
1770	| `Admin/Apps/Index` | App directory |
1771	| `Admin/Apps/Show` | Installed app detail |
1772	| `Admin/Developers/Index` | API token and webhook management |
1773
1774	### Livewire Components - Storefront
1775
1776	| Name | Purpose |
1777	|------|---------|
1778	| `Storefront/Home` | Storefront home page |
1779	| `Storefront/Collections/Index` | All collections grid |
1780	| `Storefront/Collections/Show` | Collection product grid |
1781	| `Storefront/Products/Show` | Product detail page |
1782	| `Storefront/Cart/Show` | Full cart page |
1783	| `Storefront/CartDrawer` | Slide-out cart drawer |
1784	| `Storefront/Search/Index` | Search results page |
1785	| `Storefront/Search/Modal` | Search autocomplete modal |
1786	| `Storefront/Pages/Show` | CMS page renderer |
1787	| `Storefront/Checkout/Show` | Multi-step checkout |
1788	| `Storefront/Checkout/Confirmation` | Order confirmation |
1789	| `Storefront/Account/Auth/Login` | Customer login |
1790	| `Storefront/Account/Auth/Register` | Customer registration |
1791	| `Storefront/Account/Dashboard` | Customer dashboard |
1792	| `Storefront/Account/Orders/Index` | Customer order history |
1793	| `Storefront/Account/Orders/Show` | Customer order detail |
1794	| `Storefront/Account/Addresses/Index` | Customer address management |
1795
1796	### Middleware
1797
1798	| Name | Purpose |
1799	|------|---------|
1800	| `ResolveStore` | Resolves current store from hostname or session |
1801	| `CheckStoreRole` | Validates user has required store role |
1802	| `CustomerAuthenticate` | Customer guard authentication |
1803
1804	### Policies
1805
1806	| Name | Model |
1807	|------|-------|
1808	| `ProductPolicy` | Product |
1809	| `OrderPolicy` | Order |
1810	| `CollectionPolicy` | Collection |
1811	| `DiscountPolicy` | Discount |
1812	| `CustomerPolicy` | Customer |
1813	| `StorePolicy` | Store |
1814	| `PagePolicy` | Page |
1815	| `ThemePolicy` | Theme |
1816	| `FulfillmentPolicy` | Fulfillment |
1817	| `RefundPolicy` | Refund |
1818
1819	### Form Requests
1820
1821	| Name | Purpose |
1822	|------|---------|
1823	| `StoreProductRequest` | Validate product creation |
1824	| `UpdateProductRequest` | Validate product update |
1825	| `StoreCollectionRequest` | Validate collection creation |
1826	| `UpdateCollectionRequest` | Validate collection update |
1827	| `StoreDiscountRequest` | Validate discount creation |
1828	| `UpdateDiscountRequest` | Validate discount update |
1829	| `SetCheckoutAddressRequest` | Validate checkout address |
1830	| `SetCheckoutShippingRequest` | Validate checkout shipping selection |
1831	| `CreateFulfillmentRequest` | Validate fulfillment creation |
1832	| `CreateRefundRequest` | Validate refund creation |
1833	| `StorePageRequest` | Validate page creation |
1834	| `UpdatePageRequest` | Validate page update |
1835	| `RegisterCustomerRequest` | Validate customer registration |
1836	| `StoreShippingZoneRequest` | Validate shipping zone creation |
1837	| `StoreShippingRateRequest` | Validate shipping rate creation |
1838	| `UpdateTaxSettingsRequest` | Validate tax settings update |
1839	| `StoreCustomerAddressRequest` | Validate customer address creation |
1840	| `UpdateCustomerAddressRequest` | Validate customer address update |
1841
1842	### Jobs
1843
1844	| Name | Purpose |
1845	|------|---------|
1846	| `ProcessMediaUpload` | Resize images, update media status |
1847	| `DeliverWebhook` | HTTP POST webhook with retry logic |
1848	| `AggregateAnalytics` | Daily analytics aggregation |
1849	| `CleanupAbandonedCarts` | Mark old active carts as abandoned |
1850	| `ExpireAbandonedCheckouts` | Expire timed-out checkouts, release inventory |
1851
1852	### Events
1853
1854	| Name | Purpose |
1855	|------|---------|
1856	| `OrderCreated` | Dispatched when order is created |
1857	| `OrderPaid` | Dispatched when payment is captured |
1858	| `OrderFulfilled` | Dispatched when all lines fulfilled |
1859	| `OrderCancelled` | Dispatched when order is cancelled |
1860	| `OrderRefunded` | Dispatched when refund is processed |
1861	| `CheckoutCompleted` | Dispatched when checkout completes |
1862	| `CheckoutAddressed` | Dispatched when address is set |
1863	| `CheckoutShippingSelected` | Dispatched when shipping is selected |
1864	| `ProductStatusChanged` | Dispatched when product status transitions |
1865	| `CartUpdated` | Dispatched when cart is mutated |
1866
1867	### Tests
1868
1869	**Unit tests to create (6 files):**
1870
1871	| Name | Purpose |
1872	|------|---------|
1873	| `PricingEngineTest` | Pricing pipeline unit tests |
1874	| `DiscountCalculatorTest` | Discount validation and calculation unit tests |
1875	| `TaxCalculatorTest` | Tax calculation unit tests |
1876	| `ShippingCalculatorTest` | Shipping rate calculation unit tests |
1877	| `CartVersionTest` | Cart versioning unit tests |
1878	| `HandleGeneratorTest` | Slug generation unit tests |
1879
1880	**Feature tests to create (28 files):**
1881
1882	| Name | Purpose |
1883	|------|---------|
1884	| `Tenancy/TenantResolutionTest` | Tenant resolution feature tests |
1885	| `Tenancy/StoreIsolationTest` | Store isolation feature tests |
1886	| `Auth/AdminAuthTest` | Admin authentication feature tests |
1887	| `Auth/CustomerAuthTest` | Customer authentication feature tests |
1888	| `Auth/SanctumTokenTest` | API token feature tests |
1889	| `Products/ProductCrudTest` | Product CRUD feature tests |
1890	| `Products/VariantTest` | Variant management feature tests |
1891	| `Products/InventoryTest` | Inventory management feature tests |
1892	| `Products/CollectionTest` | Collection feature tests |
1893	| `Products/MediaUploadTest` | Media upload feature tests |
1894	| `Cart/CartServiceTest` | Cart service feature tests |
1895	| `Cart/CartApiTest` | Cart API feature tests |
1896	| `Checkout/CheckoutFlowTest` | Checkout flow feature tests |
1897	| `Checkout/CheckoutStateTest` | Checkout state machine feature tests |
1898	| `Checkout/PricingIntegrationTest` | Pricing integration feature tests |
1899	| `Checkout/DiscountTest` | Checkout discount feature tests |
1900	| `Checkout/ShippingTest` | Checkout shipping feature tests |
1901	| `Checkout/TaxTest` | Checkout tax feature tests |
1902	| `Orders/OrderCreationTest` | Order creation feature tests |
1903	| `Orders/RefundTest` | Refund feature tests |
1904	| `Orders/FulfillmentTest` | Fulfillment feature tests |
1905	| `Payments/MockPaymentProviderTest` | Mock PSP charge/decline feature tests |
1906	| `Payments/PaymentServiceTest` | Payment service feature tests |
1907	| `Payments/BankTransferConfirmationTest` | Bank transfer confirmation and auto-cancel tests |
1908	| `Customers/CustomerAccountTest` | Customer account feature tests |
1909	| `Customers/AddressManagementTest` | Address management feature tests |
1910	| `Admin/DashboardTest` | Admin dashboard feature tests |
1911	| `Admin/ProductManagementTest` | Admin product management feature tests |
1912	| `Admin/OrderManagementTest` | Admin order management feature tests |
1913	| `Admin/DiscountManagementTest` | Admin discount management feature tests |
1914	| `Admin/SettingsTest` | Admin settings feature tests |
1915	| `Search/SearchTest` | Search feature tests |
1916	| `Search/AutocompleteTest` | Autocomplete feature tests |
1917	| `Analytics/EventIngestionTest` | Analytics event ingestion feature tests |
1918	| `Analytics/AggregationTest` | Analytics aggregation feature tests |
1919	| `Webhooks/WebhookDeliveryTest` | Webhook delivery feature tests |
1920	| `Webhooks/WebhookSignatureTest` | Webhook signature feature tests |
1921	| `Api/StorefrontCartApiTest` | Storefront cart API feature tests |
1922	| `Api/StorefrontCheckoutApiTest` | Storefront checkout API feature tests |
1923	| `Api/AdminProductApiTest` | Admin product API feature tests |
1924	| `Api/AdminOrderApiTest` | Admin order API feature tests |
1925
1926	---
1927
1928	## 5. Config File Changes
1929
1930	| Config File | Change | Value | Purpose |
1931	|-------------|--------|-------|---------|
1932	| `config/database.php` | SQLite pragmas | WAL, foreign keys, busy_timeout 5000, synchronous normal | Optimized SQLite configuration |
1933	| `config/auth.php` | Customer guard | Add customer guard, customer provider, customer password broker | Customer storefront auth |
1934	| `config/session.php` | Driver | File driver, lifetime 120 | Session configuration |
1935	| `config/cache.php` | Driver | File driver | Cache configuration |
1936	| `config/queue.php` | Driver | Sync driver | Queue configuration |
1937	| `config/filesystems.php` | Public disk | Media storage configuration | Public disk for media |
1938	| `config/logging.php` | JSON channel | Structured JSON channel | Structured logging |
1939
1940	### bootstrap/app.php Changes
1941
1942	- Register middleware aliases: `store.resolve`, `role.check`, `auth:customer`
1943	- Register middleware groups for `storefront` and `admin` route groups
1944	- Configure rate limiters: `login` (5/min per IP), `api.admin` (60/min per token), `api.storefront` (120/min per IP), `checkout` (10/min per session), `search` (30/min per IP), `analytics` (60/min per IP), `webhooks` (100/min per IP)
1945	- Configure exception handling
1946
1947	### Routes Files
1948
1949	| File | Contents |
1950	|------|----------|
1951	| `routes/web.php` | Admin auth routes, admin routes (prefix `/admin`), storefront routes, checkout routes, customer account routes |
1952	| `routes/api.php` | Storefront API (prefix `/api/storefront/v1`), Admin API (prefix `/api/admin/v1`), Webhook receiver routes |
1953	| `routes/console.php` | Scheduled jobs: `CleanupAbandonedCarts` daily, `ExpireAbandonedCheckouts` every 15 minutes, `AggregateAnalytics` daily, `CancelUnpaidBankTransferOrders` daily |
1954
1955	### Service Providers
1956
1957	**AppServiceProvider::boot() responsibilities:**
1958	- Bind services to container (PaymentProvider, CartService, PricingEngine, etc.)
1959	- Register rate limiters
1960	- Register global scopes
1961	- Register model observers (ProductObserver for FTS5 sync)
1962
1963	### Services Directory
1964
1965	| Service | Responsibility |
1966	|---------|---------------|
1967	| `CartService` | Cart CRUD, line management, version control, session binding |
1968	| `CheckoutService` | State machine, transitions, address/shipping/payment coordination |
1969	| `PricingEngine` | Deterministic pricing pipeline: subtotal -> discount -> shipping -> tax -> total |
1970	| `DiscountService` | Discount validation, amount calculation, line allocation |
1971	| `ShippingCalculator` | Zone matching, rate calculation |
1972	| `TaxCalculator` | Tax calculation (exclusive/inclusive), rate lookup |
1973	| `InventoryService` | Reserve, release, commit, restock operations |
1974	| `ProductService` | Product CRUD, status transitions |
1975	| `VariantMatrixService` | Cartesian product generation, variant lifecycle |
1976	| `OrderService` | Order creation from checkout, numbering, inventory commit |
1977	| `RefundService` | Refund creation, provider API, inventory restock |
1978	| `FulfillmentService` | Fulfillment CRUD, tracking, status management |
1979	| `NavigationService` | Menu tree building, URL resolution, caching |
1980	| `SearchService` | FTS5 index management, query execution, autocomplete |
1981	| `AnalyticsService` | Event tracking, daily aggregation |
1982	| `WebhookService` | Event dispatch, HMAC signing, delivery management |
1983	| `Payments\MockPaymentProvider` | In-process mock PSP: credit card (magic numbers), PayPal, bank transfer |
1984
1985	---
1986
1987	## 6. File Count Estimate
1988
1989	| Category | Estimated Files |
1990	|----------|-----------------|
1991	| Migrations | 28-32 |
1992	| Models | 32-36 |
1993	| Livewire Components (Admin) | 28-30 |
1994	| Livewire Components (Storefront) | 15-17 |
1995	| Blade Views (Admin) | 28-30 |
1996	| Blade Views (Storefront) | 18-22 |
1997	| Blade Components (Storefront) | 8-10 |
1998	| Services | 14-16 |
1999	| Policies | 10 |
2000	| Form Requests | 18-20 |
2001	| Jobs | 5 |
2002	| Events | 10 |
2003	| Middleware | 3 |
2004	| Enums | 18-20 |
2005	| Factories | 25-28 |
2006	| Seeders | 20-22 |
2007	| Config changes | 6-7 |
2008	| Routes | 3 |
2009	| Value Objects | 3-4 |
2010	| Contracts/Interfaces | 2-3 |
2011	| Traits/Concerns | 2-3 |
2012	| Scopes | 1 |
2013	| Observers | 1 |
2014	| Support classes | 2-3 |
2015	| Tests (Unit) | 6 |
2016	| Tests (Feature) | 28-30 |
2017	| Tests (Browser) | 18 files (143 tests) |
2018	| CSS/JS | 3-5 |
2019	| **Total** | **~320-380 files** |
2020
2021	---
2022
2023	## 7. Verification Checklist
2024
2025	1. Install Composer dependencies and confirm no errors
2026	2. Install npm dependencies and run the Vite build; confirm it succeeds
2027	3. Run a fresh migration on a clean SQLite database and confirm all migrations complete without errors
2028	4. Run all seeders and confirm demo data is created without errors
2029	5. Run all unit tests and confirm they pass
2030	6. Run all feature tests and confirm they pass
2031	7. Run all browser tests and confirm they pass (requires browser driver)
2032	8. Run the code style formatter/checker and confirm code conforms to project standards
2033	9. Manual smoke: visit the storefront domain and confirm it renders with products
2034	10. Manual smoke: visit the admin login page and confirm it renders; authenticate as the demo admin user
2035	11. Manual smoke: add a product to the cart, proceed through the full checkout flow
2036	12. Manual smoke: in admin, create a product, manage an order, and process a fulfillment
2037	13. List all registered routes and confirm they are correct with no duplicates
2038	14. Run the config cache command and confirm it works (verifying no `env()` calls outside config files)
2039
2040	---
2041
2042	## 8. Seeder Execution Order
2043
2044	Seeders must run in dependency order. The `DatabaseSeeder` should call them sequentially in this order:
2045
2046	1. `OrganizationSeeder`
2047	2. `StoreSeeder`
2048	3. `StoreDomainSeeder`
2049	4. `UserSeeder`
2050	5. `StoreUserSeeder`
2051	6. `StoreSettingsSeeder`
2052	7. `TaxSettingsSeeder`
2053	8. `ShippingSeeder` - creates shipping zones and rates
2054	9. `CollectionSeeder` - creates collections (before products so products can be linked)
2055	10. `ProductSeeder` - creates products with options, variants, inventory, media, and collection associations
2056	11. `DiscountSeeder`
2057	12. `CustomerSeeder` - creates customers and their addresses
2058	13. `OrderSeeder` - creates sample orders with lines, payments, fulfillments
2059	14. `ThemeSeeder` - creates themes with files and settings
2060	15. `PageSeeder`
2061	16. `NavigationSeeder` - creates navigation menus and items
2062	17. `AnalyticsSeeder`
2063	18. `SearchSettingsSeeder`
2064
2065	See spec `07-SEEDERS-AND-TEST-DATA.md` for detailed seeder data definitions.
2066

| manages store domains | add a new domain | store_domain created | happy path |

---

#### tests/Feature/Search/SearchTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| returns products matching search query | products: "Blue Cotton T-Shirt", "Red Wool Sweater"; search "cotton" | finds "Blue Cotton T-Shirt" | happy path |
| scopes search to current store | store A has "T-Shirt", store B has "T-Shirt Deluxe"; search "t-shirt" on store A | only store A product returned | authorization |
| returns empty for no matches | search "xyznonexistent" | empty results | edge case |
| logs search query for analytics | perform search | search_queries row created | happy path |
| paginates search results | 25 matching products, 12 per page | page 1 has 12, page 2 has 12, page 3 has 1 | happy path |

---

#### tests/Feature/Search/AutocompleteTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| returns suggestions matching prefix | products: "Summer Dress", "Summer Hat", "Winter Coat"; autocomplete prefix "sum" | "Summer Dress" and "Summer Hat" returned | happy path |
| limits results to configured count | 20 matching products, limit 5 | 5 results returned | happy path |
| returns empty for very short prefix | autocomplete prefix "a" (below minimum) | empty or all results | edge case |

---

#### tests/Feature/Analytics/EventIngestionTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| tracks a page view event | POST analytics event: type=page_view | analytics_events row created | happy path |
| tracks an add to cart event | POST event: type=add_to_cart, properties include product_id | event stored with properties | happy path |
| scopes events to current store | track event on store A | event.store_id = store A id | happy path |
| includes session ID when available | track event with session | session_id stored | happy path |
| includes customer ID when authenticated | track event as logged-in customer | customer_id stored | happy path |

---

#### tests/Feature/Analytics/AggregationTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| aggregates daily metrics from raw events | create events: 5 page_views, 3 add_to_carts, 2 checkout_completed; run AggregateAnalytics job | analytics_daily row with correct counts | happy path |
| calculates revenue and AOV correctly | 3 orders: 1000, 2000, 3000 cents; aggregate | revenue=6000, aov=2000 | happy path |
| runs idempotently | run aggregation twice for same date | analytics_daily row has correct values (not doubled) | edge case |

---

#### tests/Feature/Webhooks/WebhookDeliveryTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| delivers a webhook to a subscribed URL | mock HTTP client; subscription for event_type "order.created"; dispatch event | HTTP POST made to target_url | happy path |
| signs the payload with HMAC | deliver webhook | `X-Platform-Signature` header present with valid signature | happy path |
| retries failed deliveries with exponential backoff | first delivery returns 500 | job retried, attempt_count incremented | edge case |
| marks delivery as failed after max retries | 6 total attempts exhausted (1 initial + 5 retries) | delivery status = failed | edge case |
| pauses subscription after circuit breaker threshold | 5 consecutive failures | subscription status = paused | edge case |

---

#### tests/Feature/Webhooks/WebhookSignatureTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| generates a valid HMAC-SHA256 signature | payload: '{"event":"order.created"}'; secret: 'test-secret' | signature matches expected hash | happy path |
| verifies a valid signature | generate signature, then verify | returns true | happy path |
| rejects a tampered payload | sign original, verify with modified payload | returns false | validation |
| rejects an incorrect secret | sign with secret A, verify with secret B | returns false | validation |

---

#### tests/Feature/Api/StorefrontCartApiTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| creates a cart | POST /api/storefront/v1/carts | 201 response, body has cart id, version 1 | happy path |
| retrieves a cart with lines and totals | create cart with 2 lines; GET /api/storefront/v1/carts/{id} | 200 response, lines array has 2 items, totals calculated | happy path |
| adds a line to the cart | POST /api/storefront/v1/carts/{id}/lines | 200 response, line added | happy path |
| updates a line quantity | PUT /api/storefront/v1/carts/{id}/lines/{lineId} | 200 response, quantity updated | happy path |
| removes a line | DELETE /api/storefront/v1/carts/{id}/lines/{lineId} | 200 response, line removed | happy path |
| validates variant exists on add | add nonexistent variant_id | 422 response | validation |
| validates quantity is positive | add with quantity=0 | 422 response | validation |
| returns 404 for nonexistent cart | GET /api/storefront/v1/carts/999 | 404 response | validation |

---

#### tests/Feature/Api/StorefrontCheckoutApiTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| creates a checkout from a cart | POST /api/storefront/v1/checkouts with cart_id | 201 response, checkout status=started | happy path |
| sets checkout address | PUT /api/storefront/v1/checkouts/{id}/address | 200 response, status=addressed | happy path |
| selects a shipping method | PUT /api/storefront/v1/checkouts/{id}/shipping-method | 200 response, status=shipping_selected, available rates returned in response | happy path |
| applies a discount code | POST /api/storefront/v1/checkouts/{id}/apply-discount with code | 200 response, discount applied in totals | happy path |
| retrieves checkout with totals | GET /api/storefront/v1/checkouts/{id} | 200 response, includes totals_json fields | happy path |
| selects a payment method | PUT /api/storefront/v1/checkouts/{id}/payment-method with method=credit_card | 200 response, status=payment_selected, payment_method stored | happy path |
| completes checkout with credit card payment | POST /api/storefront/v1/checkouts/{id}/pay with card fields | 200 response, status=completed, order created | happy path |
| rejects payment with declined card | POST /api/storefront/v1/checkouts/{id}/pay with magic decline number | 422 response, payment declined message | validation |
| validates required address fields | PUT address with missing city | 422 response | validation |

---

#### tests/Feature/Api/AdminProductApiTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| lists products with authentication | GET /api/admin/v1/stores/{storeId}/products with Bearer token | 200 response, paginated product list | happy path |
| creates a product via API | POST /api/admin/v1/stores/{storeId}/products with product data | 201 response, product in response | happy path |
| updates a product via API | PUT /api/admin/v1/stores/{storeId}/products/{id} with updated data | 200 response, product updated | happy path |
| deletes a draft product via API | DELETE /api/admin/v1/stores/{storeId}/products/{id} | 200 response | happy path |
| requires write-products ability for mutations | token with only read-products; POST /api/admin/v1/stores/{storeId}/products | 403 response | authorization |
| returns 401 without token | GET /api/admin/v1/stores/{storeId}/products without auth header | 401 response | authorization |
| paginates results | 25 products, default page size 15 | first page has 15 items, meta has total=25 | happy path |

---

#### tests/Feature/Api/AdminOrderApiTest.php

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|
| lists orders with authentication | GET /api/admin/v1/stores/{storeId}/orders with Bearer token | 200 response, order list | happy path |
| retrieves a single order | GET /api/admin/v1/stores/{storeId}/orders/{id} | 200 response, order with lines, payments, fulfillments | happy path |
| filters orders by status | GET /api/admin/v1/stores/{storeId}/orders?status=paid | only paid orders returned | happy path |
| creates a fulfillment via API | POST /api/admin/v1/stores/{storeId}/orders/{id}/fulfillments | 201 response, fulfillment created | happy path |
| creates a refund via API | POST /api/admin/v1/stores/{storeId}/orders/{id}/refunds | 201 response, refund created | happy path |
| requires write-orders ability for mutations | token with only read-orders; POST fulfillment | 403 response | authorization |

---

## 4. Files to Create Inventory

### Models (with migration, factory, seeder)

| Name | Purpose |
|------|---------|
| `Organization` | Top-level tenant entity |
| `Store` | Individual store within an organization |
| `StoreDomain` | Domain mapping for a store |
| `StoreUser` | Pivot model with role attribute |
| `StoreSettings` | Per-store configuration |
| `Product` | Product entity |
| `ProductOption` | Product option definition |
| `ProductOptionValue` | Option value entry |
| `ProductVariant` | Product variant (SKU-level) |
| `InventoryItem` | Inventory tracking per variant |
| `Collection` | Product collection/category |
| `ProductMedia` | Product images and videos |
| `Theme` | Store theme entity |
| `ThemeFile` | Individual theme file |
| `ThemeSettings` | Theme configuration |
| `Page` | CMS page |
| `NavigationMenu` | Navigation menu entity |
| `NavigationItem` | Individual navigation item |
| `Cart` | Shopping cart entity |
| `CartLine` | Individual cart line item |
| `Checkout` | Checkout session entity |
| `ShippingZone` | Shipping zone configuration |
| `ShippingRate` | Shipping rate within a zone |
| `TaxSettings` | Per-store tax configuration |
| `Discount` | Discount code or automatic discount |
| `Customer` | Store customer entity |
| `CustomerAddress` | Customer address |
| `Order` | Order entity |
| `OrderLine` | Order line item |
| `Payment` | Payment record |
| `Refund` | Refund record |
| `Fulfillment` | Fulfillment record |
| `FulfillmentLine` | Fulfillment line item |
| `App` | Third-party app entity |
| `AppInstallation` | App installation per store |
| `OauthClient` | OAuth client for app |
| `OauthToken` | OAuth token for installation |
| `WebhookSubscription` | Webhook subscription per store |
| `WebhookDelivery` | Webhook delivery log |
| `AnalyticsEvent` | Raw analytics event |
| `AnalyticsDaily` | Pre-aggregated daily analytics |
| `SearchSettings` | Per-store search configuration |
| `SearchQuery` | Search query log |

### Livewire Components - Admin

| Name | Purpose |
|------|---------|
| `Admin/Auth/Login` | Admin login form |
| `Admin/Dashboard` | Admin dashboard |
| `Admin/Layout/Sidebar` | Admin sidebar navigation |
| `Admin/Layout/TopBar` | Admin top bar with store selector |
| `Admin/Products/Index` | Product list |
| `Admin/Products/Form` | Shared product form (create/edit mode) |
| `Admin/Collections/Index` | Collection list |
| `Admin/Collections/Form` | Shared collection form (create/edit mode) |
| `Admin/Orders/Index` | Order list |
| `Admin/Orders/Show` | Order detail |
| `Admin/Customers/Index` | Customer list |
| `Admin/Customers/Show` | Customer detail |
| `Admin/Discounts/Index` | Discount list |
| `Admin/Discounts/Form` | Shared discount form (create/edit mode) |
| `Admin/Settings/Index` | Settings tabs |
| `Admin/Settings/Shipping` | Shipping zone/rate management |
| `Admin/Settings/Taxes` | Tax settings |
| `Admin/Inventory/Index` | Inventory management - list and filter inventory items, adjust quantities (Spec 03 Section 8) |
| `Admin/Themes/Index` | Theme list |
| `Admin/Themes/Editor` | Theme editor |
| `Admin/Pages/Index` | Page list |
| `Admin/Pages/Form` | Shared page form (create/edit mode) |
| `Admin/Navigation/Index` | Navigation menu management |
| `Admin/Analytics/Index` | Analytics dashboard |
| `Admin/Search/Settings` | Search settings |
| `Admin/Apps/Index` | App directory |
| `Admin/Apps/Show` | Installed app detail |
| `Admin/Developers/Index` | API token and webhook management |

### Livewire Components - Storefront

| Name | Purpose |
|------|---------|
| `Storefront/Home` | Storefront home page |
| `Storefront/Collections/Index` | All collections grid |
| `Storefront/Collections/Show` | Collection product grid |
| `Storefront/Products/Show` | Product detail page |
| `Storefront/Cart/Show` | Full cart page |
| `Storefront/CartDrawer` | Slide-out cart drawer |
| `Storefront/Search/Index` | Search results page |
| `Storefront/Search/Modal` | Search autocomplete modal |
| `Storefront/Pages/Show` | CMS page renderer |
| `Storefront/Checkout/Show` | Multi-step checkout |
| `Storefront/Checkout/Confirmation` | Order confirmation |
| `Storefront/Account/Auth/Login` | Customer login |
| `Storefront/Account/Auth/Register` | Customer registration |
| `Storefront/Account/Dashboard` | Customer dashboard |
| `Storefront/Account/Orders/Index` | Customer order history |
| `Storefront/Account/Orders/Show` | Customer order detail |
| `Storefront/Account/Addresses/Index` | Customer address management |

### Middleware

| Name | Purpose |
|------|---------|
| `ResolveStore` | Resolves current store from hostname or session |
| `CheckStoreRole` | Validates user has required store role |
| `CustomerAuthenticate` | Customer guard authentication |

### Policies

| Name | Model |
|------|-------|
| `ProductPolicy` | Product |
| `OrderPolicy` | Order |
| `CollectionPolicy` | Collection |
| `DiscountPolicy` | Discount |
| `CustomerPolicy` | Customer |
| `StorePolicy` | Store |
| `PagePolicy` | Page |
| `ThemePolicy` | Theme |
| `FulfillmentPolicy` | Fulfillment |
| `RefundPolicy` | Refund |

### Form Requests

| Name | Purpose |
|------|---------|
| `StoreProductRequest` | Validate product creation |
| `UpdateProductRequest` | Validate product update |
| `StoreCollectionRequest` | Validate collection creation |
| `UpdateCollectionRequest` | Validate collection update |
| `StoreDiscountRequest` | Validate discount creation |
| `UpdateDiscountRequest` | Validate discount update |
| `SetCheckoutAddressRequest` | Validate checkout address |
| `SetCheckoutShippingRequest` | Validate checkout shipping selection |
| `CreateFulfillmentRequest` | Validate fulfillment creation |
| `CreateRefundRequest` | Validate refund creation |
| `StorePageRequest` | Validate page creation |
| `UpdatePageRequest` | Validate page update |
| `RegisterCustomerRequest` | Validate customer registration |
| `StoreShippingZoneRequest` | Validate shipping zone creation |
| `StoreShippingRateRequest` | Validate shipping rate creation |
| `UpdateTaxSettingsRequest` | Validate tax settings update |
| `StoreCustomerAddressRequest` | Validate customer address creation |
| `UpdateCustomerAddressRequest` | Validate customer address update |

### Jobs

| Name | Purpose |
|------|---------|
| `ProcessMediaUpload` | Resize images, update media status |
| `DeliverWebhook` | HTTP POST webhook with retry logic |
| `AggregateAnalytics` | Daily analytics aggregation |
| `CleanupAbandonedCarts` | Mark old active carts as abandoned |
| `ExpireAbandonedCheckouts` | Expire timed-out checkouts, release inventory |

### Events

| Name | Purpose |
|------|---------|
| `OrderCreated` | Dispatched when order is created |
| `OrderPaid` | Dispatched when payment is captured |
| `OrderFulfilled` | Dispatched when all lines fulfilled |
| `OrderCancelled` | Dispatched when order is cancelled |
| `OrderRefunded` | Dispatched when refund is processed |
| `CheckoutCompleted` | Dispatched when checkout completes |
| `CheckoutAddressed` | Dispatched when address is set |
| `CheckoutShippingSelected` | Dispatched when shipping is selected |
| `ProductStatusChanged` | Dispatched when product status transitions |
| `CartUpdated` | Dispatched when cart is mutated |

### Tests

**Unit tests to create (6 files):**

| Name | Purpose |
|------|---------|
| `PricingEngineTest` | Pricing pipeline unit tests |
| `DiscountCalculatorTest` | Discount validation and calculation unit tests |
| `TaxCalculatorTest` | Tax calculation unit tests |
| `ShippingCalculatorTest` | Shipping rate calculation unit tests |
| `CartVersionTest` | Cart versioning unit tests |
| `HandleGeneratorTest` | Slug generation unit tests |

**Feature tests to create (28 files):**

| Name | Purpose |
|------|---------|
| `Tenancy/TenantResolutionTest` | Tenant resolution feature tests |
| `Tenancy/StoreIsolationTest` | Store isolation feature tests |
| `Auth/AdminAuthTest` | Admin authentication feature tests |
| `Auth/CustomerAuthTest` | Customer authentication feature tests |
| `Auth/SanctumTokenTest` | API token feature tests |
| `Products/ProductCrudTest` | Product CRUD feature tests |
| `Products/VariantTest` | Variant management feature tests |
| `Products/InventoryTest` | Inventory management feature tests |
| `Products/CollectionTest` | Collection feature tests |
| `Products/MediaUploadTest` | Media upload feature tests |
| `Cart/CartServiceTest` | Cart service feature tests |
| `Cart/CartApiTest` | Cart API feature tests |
| `Checkout/CheckoutFlowTest` | Checkout flow feature tests |
| `Checkout/CheckoutStateTest` | Checkout state machine feature tests |
| `Checkout/PricingIntegrationTest` | Pricing integration feature tests |
| `Checkout/DiscountTest` | Checkout discount feature tests |
| `Checkout/ShippingTest` | Checkout shipping feature tests |
| `Checkout/TaxTest` | Checkout tax feature tests |
| `Orders/OrderCreationTest` | Order creation feature tests |
| `Orders/RefundTest` | Refund feature tests |
| `Orders/FulfillmentTest` | Fulfillment feature tests |
| `Payments/MockPaymentProviderTest` | Mock PSP charge/decline feature tests |
| `Payments/PaymentServiceTest` | Payment service feature tests |
| `Payments/BankTransferConfirmationTest` | Bank transfer confirmation and auto-cancel tests |
| `Customers/CustomerAccountTest` | Customer account feature tests |
| `Customers/AddressManagementTest` | Address management feature tests |
| `Admin/DashboardTest` | Admin dashboard feature tests |
| `Admin/ProductManagementTest` | Admin product management feature tests |
| `Admin/OrderManagementTest` | Admin order management feature tests |
| `Admin/DiscountManagementTest` | Admin discount management feature tests |
| `Admin/SettingsTest` | Admin settings feature tests |
| `Search/SearchTest` | Search feature tests |
| `Search/AutocompleteTest` | Autocomplete feature tests |
| `Analytics/EventIngestionTest` | Analytics event ingestion feature tests |
| `Analytics/AggregationTest` | Analytics aggregation feature tests |
| `Webhooks/WebhookDeliveryTest` | Webhook delivery feature tests |
| `Webhooks/WebhookSignatureTest` | Webhook signature feature tests |
| `Api/StorefrontCartApiTest` | Storefront cart API feature tests |
| `Api/StorefrontCheckoutApiTest` | Storefront checkout API feature tests |
| `Api/AdminProductApiTest` | Admin product API feature tests |
| `Api/AdminOrderApiTest` | Admin order API feature tests |

---

## 5. Config File Changes

| Config File | Change | Value | Purpose |
|-------------|--------|-------|---------|
| `config/database.php` | SQLite pragmas | WAL, foreign keys, busy_timeout 5000, synchronous normal | Optimized SQLite configuration |
| `config/auth.php` | Customer guard | Add customer guard, customer provider, customer password broker | Customer storefront auth |
| `config/session.php` | Driver | File driver, lifetime 120 | Session configuration |
| `config/cache.php` | Driver | File driver | Cache configuration |
| `config/queue.php` | Driver | Sync driver | Queue configuration |
| `config/filesystems.php` | Public disk | Media storage configuration | Public disk for media |
| `config/logging.php` | JSON channel | Structured JSON channel | Structured logging |

### bootstrap/app.php Changes

- Register middleware aliases: `store.resolve`, `role.check`, `auth:customer`
- Register middleware groups for `storefront` and `admin` route groups
- Configure rate limiters: `login` (5/min per IP), `api.admin` (60/min per token), `api.storefront` (120/min per IP), `checkout` (10/min per session), `search` (30/min per IP), `analytics` (60/min per IP), `webhooks` (100/min per IP)
- Configure exception handling

### Routes Files

| File | Contents |
|------|----------|
| `routes/web.php` | Admin auth routes, admin routes (prefix `/admin`), storefront routes, checkout routes, customer account routes |
| `routes/api.php` | Storefront API (prefix `/api/storefront/v1`), Admin API (prefix `/api/admin/v1`), Webhook receiver routes |
| `routes/console.php` | Scheduled jobs: `CleanupAbandonedCarts` daily, `ExpireAbandonedCheckouts` every 15 minutes, `AggregateAnalytics` daily, `CancelUnpaidBankTransferOrders` daily |

### Service Providers

**AppServiceProvider::boot() responsibilities:**
- Bind services to container (PaymentProvider, CartService, PricingEngine, etc.)
- Register rate limiters
- Register global scopes
- Register model observers (ProductObserver for FTS5 sync)

### Services Directory

| Service | Responsibility |
|---------|---------------|
| `CartService` | Cart CRUD, line management, version control, session binding |
| `CheckoutService` | State machine, transitions, address/shipping/payment coordination |
| `PricingEngine` | Deterministic pricing pipeline: subtotal -> discount -> shipping -> tax -> total |
| `DiscountService` | Discount validation, amount calculation, line allocation |
| `ShippingCalculator` | Zone matching, rate calculation |
| `TaxCalculator` | Tax calculation (exclusive/inclusive), rate lookup |
| `InventoryService` | Reserve, release, commit, restock operations |
| `ProductService` | Product CRUD, status transitions |
| `VariantMatrixService` | Cartesian product generation, variant lifecycle |
| `OrderService` | Order creation from checkout, numbering, inventory commit |
| `RefundService` | Refund creation, provider API, inventory restock |
| `FulfillmentService` | Fulfillment CRUD, tracking, status management |
| `NavigationService` | Menu tree building, URL resolution, caching |
| `SearchService` | FTS5 index management, query execution, autocomplete |
| `AnalyticsService` | Event tracking, daily aggregation |
| `WebhookService` | Event dispatch, HMAC signing, delivery management |
| `Payments\MockPaymentProvider` | In-process mock PSP: credit card (magic numbers), PayPal, bank transfer |

---

## 6. File Count Estimate

| Category | Estimated Files |
|----------|-----------------|
| Migrations | 28-32 |
| Models | 32-36 |
| Livewire Components (Admin) | 28-30 |
| Livewire Components (Storefront) | 15-17 |
| Blade Views (Admin) | 28-30 |
| Blade Views (Storefront) | 18-22 |
| Blade Components (Storefront) | 8-10 |
| Services | 14-16 |
| Policies | 10 |
| Form Requests | 18-20 |
| Jobs | 5 |
| Events | 10 |
| Middleware | 3 |
| Enums | 18-20 |
| Factories | 25-28 |
| Seeders | 20-22 |
| Config changes | 6-7 |
| Routes | 3 |
| Value Objects | 3-4 |
| Contracts/Interfaces | 2-3 |
| Traits/Concerns | 2-3 |
| Scopes | 1 |
| Observers | 1 |
| Support classes | 2-3 |
| Tests (Unit) | 6 |
| Tests (Feature) | 28-30 |
| Tests (Browser) | 18 files (143 tests) |
| CSS/JS | 3-5 |
| **Total** | **~320-380 files** |

---

## 7. Verification Checklist

1. Install Composer dependencies and confirm no errors
2. Install npm dependencies and run the Vite build; confirm it succeeds
3. Run a fresh migration on a clean SQLite database and confirm all migrations complete without errors
4. Run all seeders and confirm demo data is created without errors
5. Run all unit tests and confirm they pass
6. Run all feature tests and confirm they pass
7. Run all browser tests and confirm they pass (requires browser driver)
8. Run the code style formatter/checker and confirm code conforms to project standards
9. Manual smoke: visit the storefront domain and confirm it renders with products
10. Manual smoke: visit the admin login page and confirm it renders; authenticate as the demo admin user
11. Manual smoke: add a product to the cart, proceed through the full checkout flow
12. Manual smoke: in admin, create a product, manage an order, and process a fulfillment
13. List all registered routes and confirm they are correct with no duplicates
14. Run the config cache command and confirm it works (verifying no `env()` calls outside config files)

---

## 8. Seeder Execution Order

Seeders must run in dependency order. The `DatabaseSeeder` should call them sequentially in this order:

1. `OrganizationSeeder`
2. `StoreSeeder`
3. `StoreDomainSeeder`
4. `UserSeeder`
5. `StoreUserSeeder`
6. `StoreSettingsSeeder`
7. `TaxSettingsSeeder`
8. `ShippingSeeder` - creates shipping zones and rates
9. `CollectionSeeder` - creates collections (before products so products can be linked)
10. `ProductSeeder` - creates products with options, variants, inventory, media, and collection associations
11. `DiscountSeeder`
12. `CustomerSeeder` - creates customers and their addresses
13. `OrderSeeder` - creates sample orders with lines, payments, fulfillments
14. `ThemeSeeder` - creates themes with files and settings
15. `PageSeeder`
16. `NavigationSeeder` - creates navigation menus and items
17. `AnalyticsSeeder`
18. `SearchSettingsSeeder`

See spec `07-SEEDERS-AND-TEST-DATA.md` for detailed seeder data definitions.


Write

# Implementation Progress

Tracking implementation of the shop system per `specs/09-IMPLEMENTATION-ROADMAP.md`.

| Phase | Scope | Status | Tests |
|-------|-------|--------|-------|
| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | in progress | Tenancy/*, Auth/AdminAuthTest, Auth/CustomerAuthTest (cart merge deferred to P4) |
| 2 | Catalog: products, options, variants, inventory, collections, media | pending | Products/* |
| 3 | Themes, pages, navigation, storefront layout + components | pending | (rendering covered later) |
| 4 | Cart, checkout, discounts, shipping, taxes + storefront cart/checkout UI | pending | Unit/*, Cart/CartServiceTest, Checkout/* |
| 5 | Payments (mock PSP), orders, refunds, fulfillments, events | pending | Orders/*, Payments/* |
| 6 | Customer accounts | pending | Customers/*, finish CustomerAuthTest |
| 7 | Admin panel (all sections) | pending | Admin/* |
| API | Spec 02 REST APIs + Sanctum | pending | Api/*, Auth/SanctumTokenTest, Cart/CartApiTest |
| 8 | Search (FTS5) | pending | Search/* |
| 9 | Analytics | pending | Analytics/* |
| 10 | Apps & webhooks | pending | Webhooks/* |
| 11 | Polish, seeders (spec 07), error pages, dark mode, a11y | pending | - |
| 12 | Full suite + browser tests (spec 08) + Playwright MCP acceptance verification | pending | Browser/* |

## Log

- 2026-06-09: Project start. Fresh Livewire starter kit (Fortify). Created progress tracker, started Phase 1.


File created successfully at: /Users/fabianwesner/Herd/shop/specs/progress.md (file state is current in your context — no need to Read it back)

# Implementation Progress

Tracking implementation of the shop system per `specs/09-IMPLEMENTATION-ROADMAP.md`.

| Phase | Scope | Status | Tests |
|-------|-------|--------|-------|
| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | in progress | Tenancy/*, Auth/AdminAuthTest, Auth/CustomerAuthTest (cart merge deferred to P4) |
| 2 | Catalog: products, options, variants, inventory, collections, media | pending | Products/* |
| 3 | Themes, pages, navigation, storefront layout + components | pending | (rendering covered later) |
| 4 | Cart, checkout, discounts, shipping, taxes + storefront cart/checkout UI | pending | Unit/*, Cart/CartServiceTest, Checkout/* |
| 5 | Payments (mock PSP), orders, refunds, fulfillments, events | pending | Orders/*, Payments/* |
| 6 | Customer accounts | pending | Customers/*, finish CustomerAuthTest |
| 7 | Admin panel (all sections) | pending | Admin/* |
| API | Spec 02 REST APIs + Sanctum | pending | Api/*, Auth/SanctumTokenTest, Cart/CartApiTest |
| 8 | Search (FTS5) | pending | Search/* |
| 9 | Analytics | pending | Analytics/* |
| 10 | Apps & webhooks | pending | Webhooks/* |
| 11 | Polish, seeders (spec 07), error pages, dark mode, a11y | pending | - |
| 12 | Full suite + browser tests (spec 08) + Playwright MCP acceptance verification | pending | Browser/* |

## Log

- 2026-06-09: Project start. Fresh Livewire starter kit (Fortify). Created progress tracker, started Phase 1.


Agent

Implement Phase 1 Foundation

Prompt:
You are implementing Phase 1 (Foundation) of a multi-tenant e-commerce platform in /Users/fabianwesner/Herd/shop (Laravel 12, Livewire v4, Flux UI Free v2, Tailwind v4, SQLite, Pest v4). The repo is a fresh Livewire starter kit with Fortify. Work on the current branch; never create branches or commit (the orchestrator commits).

REQUIRED READING (read these fully before writing code):
- specs/09-IMPLEMENTATION-ROADMAP.md — Section "Phase 1: Foundation" (lines ~74-243), plus test spec sections for tests/Feature/Tenancy/TenantResolutionTest.php, StoreIsolationTest.php, Auth/AdminAuthTest.php, Auth/CustomerAuthTest.php (lines ~1119-1199), and Section 5 "Config File Changes" (lines ~1928-1984).
- specs/01-DATABASE-SCHEMA.md — sections for organizations, stores, store_domains, users, store_users, store_settings tables (exact columns/constraints).
- specs/06-AUTH-AND-SECURITY.md — full file (admin auth, customer auth, guards, rate limiting, policies, permission matrix).

DELIVERABLES (per roadmap Phase 1, steps 1.1-1.8):
1. Config: .env + config changes (sqlite WAL/foreign keys/busy_timeout pragmas in config/database.php, file cache/session, sync queue, log mail, customer guard + customers provider + customers password broker in config/auth.php, structured JSON log channel).
2. Migrations batch 1-2: organizations, stores, store_domains, modify users (add status, last_login_at; rename password column to password_hash per spec 01/06 — adapt Fortify/starter code accordingly), store_users pivot, store_settings. Money as INTEGER cents, enums as TEXT with CHECK constraints.
3. Models: Organization, Store, StoreDomain, StoreUser (custom Pivot), StoreSettings + relationships, casts() methods, factories with meaningful defaults, seeders (OrganizationSeeder, StoreSeeder, StoreDomainSeeder, UserSeeder, StoreUserSeeder, StoreSettingsSeeder — wire into DatabaseSeeder; consult specs/07-SEEDERS-AND-TEST-DATA.md for the demo data definitions for these tables). IMPORTANT: the demo store MUST have a storefront store_domain with hostname `shop.test` (the Herd-served hostname) so the locally served site resolves the tenant; add spec-defined domains as additional rows.
4. Enums: StoreStatus, StoreUserRole, StoreDomainType (TitleCase cases, string-backed lowercase values per spec).
5. Middleware App\Http\Middleware\ResolveStore: storefront = hostname → store_domains lookup with 5-min cache (key `store_domain:{hostname}`), binds Store singleton as container instance `current_store`; admin = session `current_store_id` + verify store_users membership (403 if not member); 404 unknown hostname; 503 suspended store on storefront. Register `storefront` and `admin` middleware groups + aliases in bootstrap/app.php.
6. BelongsToStore trait (app/Models/Concerns) + StoreScope (app/Models/Scopes): global scope on store_id from `current_store`, auto-set store_id on creating.
7. Auth: Admin Livewire Login at /admin/login (guard web, rate limit `login` 5/min/IP registered in AppServiceProvider, session regeneration, last_login_at update, remember me, generic "Invalid credentials" error, logout POST /admin/logout invalidating session). Customer guard `customer` + CustomerUserProvider scoping by current store's store_id; Customer model stub may be needed for the guard config (full customer features come in later phases — create the Customer model + customers table migration ONLY if spec 01 places it in batch 1; otherwise configure the guard to be wired when the model lands in Phase 5; check spec 01 and decide — if in doubt create the customers + customer_addresses migrations/models now per spec 01 so the guard works and CustomerAuthTest can pass). Customer login/register Livewire components under Storefront\Account\Auth with views (storefront layout can be minimal for now; Phase 3 builds the real layout).
8. Policies: ProductPolicy, OrderPolicy, CollectionPolicy, DiscountPolicy, CustomerPolicy, StorePolicy, PagePolicy, ThemePolicy, FulfillmentPolicy, RefundPolicy implementing the permission matrix from the roadmap (role via User::roleForStore(Store): ?StoreUserRole). Policies for models that don't exist yet should still be created referencing the model class names that WILL exist; create empty model stubs only if needed to keep things working — prefer deferring policy registration for not-yet-existing models if it breaks; use judgment, but the permission matrix logic must be centralized (e.g., a HasStorePermissions concern) so later phases reuse it.
9. Starter kit adaptation: the spec replaces starter auth/dashboard with /admin and storefront flows. Adapt Fortify to the password_hash column (getAuthPasswordName). Remove or adapt starter routes/views/tests that conflict with the spec (e.g., tests/Feature/Auth/*, Settings/*, DashboardTest from the starter kit may be deleted/replaced — this is approved). Keep two-factor columns/features only if spec 06 requires them.
10. Pest helpers in tests/Pest.php: createStoreContext() (creates Organization, Store, StoreDomain, User with Owner role, binds current_store container instance), actingAsAdmin(User $user) (auth + session current_store_id), actingAsCustomer(Customer $customer) (customer guard).
11. Tests (write EXACTLY the test cases from the roadmap tables): tests/Feature/Tenancy/TenantResolutionTest.php, tests/Feature/Tenancy/StoreIsolationTest.php (StoreIsolation tests reference Product/Order models which don't exist yet — implement the store-scoping tests using a real scoped model; if Product/Order don't exist yet, write the tests against a minimal approach: SKIP是not allowed. Better: write StoreIsolationTest now using Product, and create a minimal Product model + products migration per spec 01 ONLY if that's cleaner — NO: Phase 2 owns products. Instead write StoreIsolationTest with a dedicated test-only scoped model OR defer the Product/Order-specific cases by writing them now but marked ->todo(). Decision: write the file with the Product/Order cases as ->todo('Phase 2/5') and fully implement the cases that can run (auto store_id, scope removal) using whichever store-scoped table exists; Phase 2/5 agents will un-todo them), tests/Feature/Auth/AdminAuthTest.php (all 10 cases), tests/Feature/Auth/CustomerAuthTest.php (all cases EXCEPT the guest-cart merge case — add it as ->todo('Phase 4')).

QUALITY BARS:
- php artisan migrate:fresh --seed must succeed.
- All new tests pass: php artisan test --compact (full suite green; starter tests removed/adapted as needed).
- vendor/bin/pint --dirty --format agent clean.
- Explicit return types, casts() methods, PHP 8.4 style, curly braces always, no em-dashes anywhere.
- Use `php artisan make:*` generators with --no-interaction.

Your final message: list of files created/changed (grouped), key decisions made (especially around Fortify/password_hash, customer guard timing, starter kit removals), seeded demo credentials, and the full test run result summary.


Phase 1 (Foundation) is complete. Full suite green, Pint clean, `migrate:fresh --seed` succeeds, and the live Herd site at http://shop.test resolves the demo tenant ("Acme Fashion") with working /admin/login and /account/login pages.

## Files created/changed

**Config / bootstrap**
- `config/database.php` (SQLite: WAL, busy_timeout 5000, synchronous normal, FKs on), `config/auth.php` (customer guard, customers provider with custom `customer-eloquent` driver, customers password broker on `customer_password_reset_tokens`), `config/logging.php` (`structured` daily JSON channel)
- `bootstrap/app.php`: `storefront` and `admin` middleware groups (`ResolveStore:storefront` / `ResolveStore:admin`), `store.resolve` alias, `redirectGuestsTo` (admin paths to /admin/login, others to /account/login), `redirectUsersTo('/admin')`
- `.env` already had sqlite/file cache/file session/sync queue/log mail; unchanged

**Migrations** (all enums via `enum()` which compiles to TEXT + CHECK on SQLite; booleans as integer; money convention noted for later phases)
- Modified `0001_01_01_000000_create_users_table.php`: `password` renamed to `password_hash`, added `status` (active/disabled + index), `last_login_at`; two-factor migration retargeted to `after('password_hash')`
- New: `organizations`, `stores`, `store_domains` (no updated_at), `store_users` (composite PK + role), `store_settings` (store_id PK), `customers` (unique store_id+email), `customer_addresses` (no timestamps), `customer_password_reset_tokens` (store_id+email PK)

**Enums**: `App\Enums\StoreStatus`, `StoreUserRole`, `StoreDomainType` (TitleCase cases, lowercase string values)

**Models**: `Organization`, `Store`, `StoreDomain`, `StoreUser` (custom Pivot, role cast), `StoreSettings` (settings_json array cast), `Customer`, `CustomerAddress`; `User` updated (`password_hash` hashed cast, `getAuthPasswordName()`/`getAuthPassword()`, `stores()` belongsToMany, `roleForStore(Store): ?StoreUserRole`). Plus `App\Models\Concerns\BelongsToStore` and `App\Models\Scopes\StoreScope` (scopes on container-bound `current_store`, auto-sets store_id on creating)

**Tenancy/Auth plumbing**: `App\Http\Middleware\ResolveStore` (hostname lookup with 5-min cache key `store_domain:{hostname}`, 404 unknown host, 503 suspended storefront, binds `current_store` instance + shares `currentStore` with views; admin context reads session `current_store_id`, verifies store_users membership, 403 otherwise), `App\Auth\CustomerUserProvider` (injects store_id into `retrieveByCredentials` and `retrieveById`, disables remember tokens)

**Controllers/Livewire/views**: `Admin\Auth\LoginController` (store/destroy), `Storefront\Auth\CustomerLoginController`, `CustomerRegisterController`; Livewire pages `Admin\Auth\Login`, `Storefront\Account\Auth\Login` and `Register` with Flux-based views; minimal `layouts/storefront`, `storefront/home`, `storefront/account/index`, `admin/dashboard` views; `routes/web.php` rewritten

**Authorization**: `App\Traits\ChecksStoreRole` (getStoreRole/hasRole/isOwnerOrAdmin/isOwnerAdminOrStaff/isAnyRole/currentStoreId, per spec 06) + 10 policies (Product, Order, Collection, Discount, Customer, Store, Page, Theme, Fulfillment, Refund) implementing the permission matrix

**Factories/Seeders**: Organization/Store(suspended)/StoreDomain(admin,api,secondary)/StoreSettings/Customer(guest)/CustomerAddress factories; UserFactory adapted (password_hash, status, last_login_at, disabled state); seeders Organization, Store, StoreDomain, User, StoreUser, StoreSettings wired into DatabaseSeeder, all idempotent (updateOrCreate)

**Tests**: `tests/Pest.php` (RefreshDatabase for Feature, `createStoreContext()`, `actingAsAdmin()`, `actingAsCustomer()`), `tests/Feature/Tenancy/TenantResolutionTest.php` (6 cases), `StoreIsolationTest.php` (Product/Order cases as todo for Phase 2/5; scoping, auto store_id, direct-ID block, scope removal implemented against Customer), `tests/Feature/Auth/AdminAuthTest.php` (all 10 cases), `CustomerAuthTest.php` (9 cases + cart-merge as todo Phase 4)

## Key decisions
- **Fortify**: kept installed but `Fortify::ignoreRoutes()` in `FortifyServiceProvider::register()`; spec's auth is custom (/admin/login with 429 rate limiting and generic "Invalid credentials", which Fortify's pipeline cannot produce). Fortify actions adapted to `password_hash`; the `hashed` cast on `password_hash` plus `getAuthPasswordName()` makes `Auth::attempt()` work. Admin login also requires `status = 'active'`. Two-factor columns/trait retained per spec 01. Admin password reset flow deferred (no Phase 1 test coverage); `customers` broker config and token table are in place for Phase 5.
- **Customer guard now**: created `customers`/`customer_addresses` migrations + models in Phase 1 (spec 01 defines them; needed for the guard and CustomerAuthTest). Customers have no `remember_token` column, so `getRememberTokenName()` returns `''` and the provider's `retrieveByToken()` returns null.
- **POST endpoints**: roadmap tests POST directly to /admin/login etc., so Livewire components render the forms (classic POST + @csrf) and thin controllers handle submission under `throttle:login` (5/min/IP, registered in AppServiceProvider), giving real 429s.
- **Starter kit removals** (approved): starter auth/settings/dashboard tests, Settings Livewire components + views, Fortify auth views, dashboard/welcome views, routes/settings.php. Starter app/auth layouts kept for later admin phases.
- **Seeded domains** include the required `shop.test` storefront row for Acme Fashion (Herd hostname) alongside spec domains acme-fashion.test, admin.acme-fashion.test, acme-electronics.test.

## Demo credentials (all password: `password`)
admin@acme.test (Fashion owner), manager@acme.test (Fashion admin), staff@acme.test (Fashion staff), support@acme.test (Fashion support), admin2@acme.test (Electronics owner)

## Test results
`php artisan test --compact`: **30 passed, 3 todos (86 assertions)**, 0 failures. `vendor/bin/pint --dirty --format agent`: pass. `php artisan migrate:fresh --seed`: success. Live smoke check: http://shop.test/ resolves Acme Fashion (200), /admin/login and /account/login both 200. Note: the sandbox network allowlist blocked curl to shop.test; I ran that one smoke check unsandboxed (manageable via /sandbox).

agentId: aee658067d5c9624e (use SendMessage with to: 'aee658067d5c9624e' to continue this agent)
<usage>subagent_tokens: 184970
tool_uses: 137
duration_ms: 1078685</usage>

Prompt:
You are implementing Phase 1 (Foundation) of a multi-tenant e-commerce platform in /Users/fabianwesner/Herd/shop (Laravel 12, Livewire v4, Flux UI Free v2, Tailwind v4, SQLite, Pest v4). The repo is a fresh Livewire starter kit with Fortify. Work on the current branch; never create branches or commit (the orchestrator commits).

REQUIRED READING (read these fully before writing code):
- specs/09-IMPLEMENTATION-ROADMAP.md — Section "Phase 1: Foundation" (lines ~74-243), plus test spec sections for tests/Feature/Tenancy/TenantResolutionTest.php, StoreIsolationTest.php, Auth/AdminAuthTest.php, Auth/CustomerAuthTest.php (lines ~1119-1199), and Section 5 "Config File Changes" (lines ~1928-1984).
- specs/01-DATABASE-SCHEMA.md — sections for organizations, stores, store_domains, users, store_users, store_settings tables (exact columns/constraints).
- specs/06-AUTH-AND-SECURITY.md — full file (admin auth, customer auth, guards, rate limiting, policies, permission matrix).

DELIVERABLES (per roadmap Phase 1, steps 1.1-1.8):
1. Config: .env + config changes (sqlite WAL/foreign keys/busy_timeout pragmas in config/database.php, file cache/session, sync queue, log mail, customer guard + customers provider + customers password broker in config/auth.php, structured JSON log channel).
2. Migrations batch 1-2: organizations, stores, store_domains, modify users (add status, last_login_at; rename password column to password_hash per spec 01/06 — adapt Fortify/starter code accordingly), store_users pivot, store_settings. Money as INTEGER cents, enums as TEXT with CHECK constraints.
3. Models: Organization, Store, StoreDomain, StoreUser (custom Pivot), StoreSettings + relationships, casts() methods, factories with meaningful defaults, seeders (OrganizationSeeder, StoreSeeder, StoreDomainSeeder, UserSeeder, StoreUserSeeder, StoreSettingsSeeder — wire into DatabaseSeeder; consult specs/07-SEEDERS-AND-TEST-DATA.md for the demo data definitions for these tables). IMPORTANT: the demo store MUST have a storefront store_domain with hostname `shop.test` (the Herd-served hostname) so the locally served site resolves the tenant; add spec-defined domains as additional rows.
4. Enums: StoreStatus, StoreUserRole, StoreDomainType (TitleCase cases, string-backed lowercase values per spec).
5. Middleware App\Http\Middleware\ResolveStore: storefront = hostname → store_domains lookup with 5-min cache (key `store_domain:{hostname}`), binds Store singleton as container instance `current_store`; admin = session `current_store_id` + verify store_users membership (403 if not member); 404 unknown hostname; 503 suspended store on storefront. Register `storefront` and `admin` middleware groups + aliases in bootstrap/app.php.
6. BelongsToStore trait (app/Models/Concerns) + StoreScope (app/Models/Scopes): global scope on store_id from `current_store`, auto-set store_id on creating.
7. Auth: Admin Livewire Login at /admin/login (guard web, rate limit `login` 5/min/IP registered in AppServiceProvider, session regeneration, last_login_at update, remember me, generic "Invalid credentials" error, logout POST /admin/logout invalidating session). Customer guard `customer` + CustomerUserProvider scoping by current store's store_id; Customer model stub may be needed for the guard config (full customer features come in later phases — create the Customer model + customers table migration ONLY if spec 01 places it in batch 1; otherwise configure the guard to be wired when the model lands in Phase 5; check spec 01 and decide — if in doubt create the customers + customer_addresses migrations/models now per spec 01 so the guard works and CustomerAuthTest can pass). Customer login/register Livewire components under Storefront\Account\Auth with views (storefront layout can be minimal for now; Phase 3 builds the real layout).
8. Policies: ProductPolicy, OrderPolicy, CollectionPolicy, DiscountPolicy, CustomerPolicy, StorePolicy, PagePolicy, ThemePolicy, FulfillmentPolicy, RefundPolicy implementing the permission matrix from the roadmap (role via User::roleForStore(Store): ?StoreUserRole). Policies for models that don't exist yet should still be created referencing the model class names that WILL exist; create empty model stubs only if needed to keep things working — prefer deferring policy registration for not-yet-existing models if it breaks; use judgment, but the permission matrix logic must be centralized (e.g., a HasStorePermissions concern) so later phases reuse it.
9. Starter kit adaptation: the spec replaces starter auth/dashboard with /admin and storefront flows. Adapt Fortify to the password_hash column (getAuthPasswordName). Remove or adapt starter routes/views/tests that conflict with the spec (e.g., tests/Feature/Auth/*, Settings/*, DashboardTest from the starter kit may be deleted/replaced — this is approved). Keep two-factor columns/features only if spec 06 requires them.
10. Pest helpers in tests/Pest.php: createStoreContext() (creates Organization, Store, StoreDomain, User with Owner role, binds current_store container instance), actingAsAdmin(User $user) (auth + session current_store_id), actingAsCustomer(Customer $customer) (customer guard).
11. Tests (write EXACTLY the test cases from the roadmap tables): tests/Feature/Tenancy/TenantResolutionTest.php, tests/Feature/Tenancy/StoreIsolationTest.php (StoreIsolation tests reference Product/Order models which don't exist yet — implement the store-scoping tests using a real scoped model; if Product/Order don't exist yet, write the tests against a minimal approach: SKIP是not allowed. Better: write StoreIsolationTest now using Product, and create a minimal Product model + products migration per spec 01 ONLY if that's cleaner — NO: Phase 2 owns products. Instead write StoreIsolationTest with a dedicated test-only scoped model OR defer the Product/Order-specific cases by writing them now but marked ->todo(). Decision: write the file with the Product/Order cases as ->todo('Phase 2/5') and fully implement the cases that can run (auto store_id, scope removal) using whichever store-scoped table exists; Phase 2/5 agents will un-todo them), tests/Feature/Auth/AdminAuthTest.php (all 10 cases), tests/Feature/Auth/CustomerAuthTest.php (all cases EXCEPT the guest-cart merge case — add it as ->todo('Phase 4')).

QUALITY BARS:
- php artisan migrate:fresh --seed must succeed.
- All new tests pass: php artisan test --compact (full suite green; starter tests removed/adapted as needed).
- vendor/bin/pint --dirty --format agent clean.
- Explicit return types, casts() methods, PHP 8.4 style, curly braces always, no em-dashes anywhere.
- Use `php artisan make:*` generators with --no-interaction.

Your final message: list of files created/changed (grouped), key decisions made (especially around Fortify/password_hash, customer guard timing, starter kit removals), seeded demo credentials, and the full test run result summary.


Phase 1 is done (30 passed, 3 todos). Let me verify the suite myself, then commit and launch Phase 2.


Bash

php artisan test --compact 2>&1 | tail -5 && git add -A && git commit -q -m "Phase 1: Foundation - tenancy, multi-store models, ResolveStore middleware, admin/customer auth, policies

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>" && git log --oneline -1

Verify tests pass and commit Phase 1


  ....................ttt..........

  Tests:    3 todos, 30 passed (86 assertions)
  Duration: 3.48s

5b743cde Phase 1: Foundation - tenancy, multi-store models, ResolveStore middleware, admin/customer auth, policies


Edit

| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | in progress | Tenancy/*, Auth/AdminAuthTest, Auth/CustomerAuthTest (cart merge deferred to P4) |
| 2 | Catalog: products, options, variants, inventory, collections, media | pending | Products/* |

| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | DONE (commit 5b743cde) | 30 passed, 3 todos |
| 2 | Catalog: products, options, variants, inventory, collections, media | in progress | Products/* |


The file /Users/fabianwesner/Herd/shop/specs/progress.md has been updated successfully. (file state is current in your context — no need to Read it back)

| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | DONE (commit 5b743cde) | 30 passed, 3 todos |
| 2 | Catalog: products, options, variants, inventory, collections, media | in progress | Products/* |

# Implementation Progress

Tracking implementation of the shop system per `specs/09-IMPLEMENTATION-ROADMAP.md`.

| Phase | Scope | Status | Tests |
|-------|-------|--------|-------|
| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | in progress | Tenancy/*, Auth/AdminAuthTest, Auth/CustomerAuthTest (cart merge deferred to P4) |
| 2 | Catalog: products, options, variants, inventory, collections, media | pending | Products/* |
| 3 | Themes, pages, navigation, storefront layout + components | pending | (rendering covered later) |
| 4 | Cart, checkout, discounts, shipping, taxes + storefront cart/checkout UI | pending | Unit/*, Cart/CartServiceTest, Checkout/* |
| 5 | Payments (mock PSP), orders, refunds, fulfillments, events | pending | Orders/*, Payments/* |
| 6 | Customer accounts | pending | Customers/*, finish CustomerAuthTest |
| 7 | Admin panel (all sections) | pending | Admin/* |
| API | Spec 02 REST APIs + Sanctum | pending | Api/*, Auth/SanctumTokenTest, Cart/CartApiTest |
| 8 | Search (FTS5) | pending | Search/* |
| 9 | Analytics | pending | Analytics/* |
| 10 | Apps & webhooks | pending | Webhooks/* |
| 11 | Polish, seeders (spec 07), error pages, dark mode, a11y | pending | - |
| 12 | Full suite + browser tests (spec 08) + Playwright MCP acceptance verification | pending | Browser/* |

## Log

- 2026-06-09: Project start. Fresh Livewire starter kit (Fortify). Created progress tracker, started Phase 1.

-| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | in progress | Tenancy/*, Auth/AdminAuthTest, Auth/CustomerAuthTest (cart merge deferred to P4) |

-| 2 | Catalog: products, options, variants, inventory, collections, media | pending | Products/* |

+| 1 | Foundation: config, tenancy migrations/models, enums, ResolveStore, BelongsToStore, admin+customer auth, policies, Pest helpers | DONE (commit 5b743cde) | 30 passed, 3 todos |

+| 2 | Catalog: products, options, variants, inventory, collections, media | in progress | Products/* |


Agent

Implement Phase 2 Catalog

Prompt:
You are implementing Phase 2 (Catalog) of a multi-tenant e-commerce platform in /Users/fabianwesner/Herd/shop (Laravel 12, Livewire v4, SQLite, Pest v4). Phase 1 (tenancy, stores, auth, policies, BelongsToStore trait, StoreScope, Pest helpers createStoreContext()/actingAsAdmin()/actingAsCustomer()) is complete and committed. Work on the current branch; never create branches or commit.

REQUIRED READING (fully, before coding):
- specs/09-IMPLEMENTATION-ROADMAP.md — "Phase 2: Catalog" section (lines ~245-347) and the test spec tables for tests/Feature/Products/ProductCrudTest.php, VariantTest.php, InventoryTest.php, CollectionTest.php, MediaUploadTest.php and tests/Unit/HandleGeneratorTest.php (lines ~1104-1275).
- specs/01-DATABASE-SCHEMA.md — sections for products, product_options, product_option_values, product_variants, variant_option_values, inventory_items, collections, collection_products, product_media (exact columns, constraints, indexes).
- specs/05-BUSINESS-LOGIC.md — product lifecycle/state machine, variant matrix, inventory rules, handle generation sections.
- specs/07-SEEDERS-AND-TEST-DATA.md — CollectionSeeder and ProductSeeder definitions.
- Study existing Phase 1 conventions first: app/Models (Store, Customer etc.), app/Models/Concerns/BelongsToStore.php, app/Enums, tests/Pest.php, database/seeders.

DELIVERABLES:
1. Migrations in dependency order: products, product_options, product_option_values, product_variants, variant_option_values (composite PK), inventory_items, collections, collection_products (composite PK), product_media. Money INTEGER cents, enums TEXT+CHECK, follow spec 01 exactly (indexes, uniques like SKU unique per store, handle unique per store).
2. Models with relationships per roadmap: Product, ProductOption, ProductOptionValue, ProductVariant, InventoryItem, Collection, ProductMedia (+ pivot setup for collection_products with position, variant_option_values). BelongsToStore on products, collections, inventory_items (per Phase 1 trait list). Factories with meaningful defaults + useful states (e.g., active(), draft(), archived()).
3. Enums: ProductStatus, VariantStatus, CollectionStatus, MediaType, MediaStatus, InventoryPolicy.
4. Services:
   - App\Services\ProductService: create (nested options/variants, auto default variant + inventory item), update, transitionStatus (state machine per spec 05: draft→active requires priced variant; active→draft blocked when order lines exist; active→archived; etc.), delete (hard delete only draft w/o order refs, else throw).
   - App\Services\VariantMatrixService: rebuildMatrix (cartesian product, preserve existing variants + prices, archive orphans with order refs, delete orphans without).
   - App\Support\HandleGenerator: generate(string $title, string $table, int $storeId, ?int $excludeId = null): string — slugify, collision suffix -1, -2, store-scoped, exclude current id.
   - App\Services\InventoryService: checkAvailability, reserve, release, commit, restock — DB transactions, InsufficientInventoryException (app/Exceptions) when policy deny and available < qty.
5. Media: App\Jobs\ProcessMediaUpload (resize to thumbnail 150x150, medium 600x600, large 1200x1200 — use GD via Laravel/native PHP, do NOT add composer dependencies; store on public disk; status processing→ready/failed).
6. Seeders: CollectionSeeder then ProductSeeder per spec 07 (products with options, variants, inventory, media placeholders, collection associations) wired into DatabaseSeeder after existing seeders. Must be idempotent enough that migrate:fresh --seed works.
7. Un-todo the Product cases in tests/Feature/Tenancy/StoreIsolationTest.php (scopes product queries; prevents cross-store access via direct ID etc. — implement the product-based cases now; leave Order case as todo for Phase 5).
8. Tests — implement EXACTLY the cases in the roadmap tables:
   - tests/Unit/HandleGeneratorTest.php (6 cases)
   - tests/Feature/Products/ProductCrudTest.php (13 cases; admin list cases can assert via Livewire or HTTP as appropriate given Phase 7 admin UI doesn't exist yet — for "lists products for the current store" you may test the underlying query/service or mark route-dependent assertions to be strengthened in Phase 7; prefer testing ProductService + DB now. If a case strictly needs admin UI (GET /admin/products), implement a minimal placeholder route only if trivial, otherwise ->todo('Phase 7') that single case)
   - tests/Feature/Products/VariantTest.php (8 cases)
   - tests/Feature/Products/InventoryTest.php (8 cases)
   - tests/Feature/Products/CollectionTest.php (7 cases)
   - tests/Feature/Products/MediaUploadTest.php (6 cases; Livewire upload cases: test the job + a service/action level; if a case strictly needs the Phase 7 admin form, use Livewire component only if you create it now — otherwise test via storage fake + job + model layer and keep the intent of each case)
   Note: order-reference-dependent cases (e.g., "prevents deletion of product with order references") need an order_lines table that doesn't exist until Phase 5 — for those, check spec 01 for order_lines and decide: EITHER mark ->todo('Phase 5') OR implement the check defensively (e.g., Schema::hasTable guard) — prefer ->todo('Phase 5') and implement the service check so it activates when the table exists. Keep a clear list of deferred cases in your final report.

QUALITY BARS:
- php artisan migrate:fresh --seed succeeds.
- Full suite green: php artisan test --compact.
- vendor/bin/pint --dirty --format agent clean.
- Explicit return types, casts() methods, PHP 8.4, curly braces always, no em-dashes. Use php artisan make:* generators.

Final message: files created/changed (grouped), key decisions, list of test cases deferred as todo (with phase), full test summary.


<command-name>/remote-control</command-name>
            <command-message>remote-control</command-message>
            <command-args></command-args>


<local-command-stdout>Remote Control connecting…</local-command-stdout>