# CartWise v1 — AI Agent Build Specification ## 1. Mission ## 1A. Backlog and Scrum Tracking The living Scrum backlog for this repository is `docs/scrum-backlog.md`. Agents must use it as the primary planning artifact for implementation work: - review the relevant epic, story, and tasks before starting substantial feature work - update `docs/scrum-backlog.md` when work is selected, split, deferred, blocked, or completed - mark completed work in the backlog as soon as the code, tests, and verification for that item are done - add newly discovered implementation tasks under the appropriate story rather than tracking them only in chat - keep backlog status aligned with the actual repository state If implementation order changes to unblock work, update `docs/scrum-backlog.md` to reflect that decision and keep the reasoning brief and explicit. Agents must also consult: - `docs/decision-log.md` for accepted product and delivery decisions - `docs/release-mvp-checklist.md` for the current MVP completion criteria ## 1B. Current MVP-Now Delivery Contract The full CartWise v1 vision in this file remains the long-term target architecture and product direction. However, the **current active delivery target** is a **local MVP by September 10, 2026** for a **solo founder + AI** workflow. For the current MVP, prioritize only the smallest launchable grocery-memory slice: 1. Register and sign in. 2. Create and use a household. 3. Maintain one active grocery list. 4. Add free-text grocery items without requiring product resolution. 5. Toggle, skip, edit, and delete list items as required by the backlog. 6. Record purchases manually. 7. View basic personal price history. 8. Show deterministic running-low suggestions. 9. Ensure core flows work on a phone-sized layout. For the current MVP, these are explicitly **not launch blockers** unless the user reprioritizes them: - barcode scanning - camera scan workflows - Open Food Facts or USDA integration - dedicated shopping mode screens beyond what the list page can cover - hosted deployment - production PostgreSQL setup When this file and `docs/scrum-backlog.md` differ on near-term execution priority, use `docs/scrum-backlog.md` as the active delivery plan and keep this file as the broader product specification. Build **CartWise v1**, a mobile-first grocery companion web application using **ASP.NET Core MVC**, **Razor Views**, **HTML5**, **CSS3**, **jQuery**, and **vanilla JavaScript**. CartWise v1 is not a grocery delivery platform and is not tied to any retailer. Its first job is to become the shopper's grocery memory: 1. Maintain shared household grocery lists. 2. Resolve generic grocery concepts such as “milk” or “peanut butter” to preferred products. 3. Scan UPC/GTIN barcodes and identify products. 4. Record what the household purchased, where, and for how much. 5. Build personal price history. 6. Show useful price intelligence such as normal price, recent price, lowest observed price, and unit price. 7. Suggest items that may be running low using purchase intervals. 8. Provide a simple, touch-friendly shopping mode that works well on a phone. Do **not** expand v1 into meal planning, coupon clipping, universal retailer checkout, indoor navigation, or full cross-retailer real-time price comparison unless explicitly requested. --- ## 2. Non-Negotiable Technology Choices ### Application - ASP.NET Core MVC - C# - Razor Views - HTML5 - CSS3 - jQuery for concise DOM manipulation, event handling, AJAX, and progressive enhancement where it improves readability - Vanilla JavaScript for browser-native APIs, focused modules, and cases where jQuery adds no value - Entity Framework Core - PostgreSQL ### MVP Development Note - SQLite is acceptable and preferred for the current local MVP timeline - keep the design compatible with a later PostgreSQL move ### Optional / Later - PostgreSQL PostGIS when geographic querying becomes necessary - IndexedDB + Service Worker for advanced offline/PWA functionality - Background jobs for enrichment/sync - Redis only if profiling demonstrates a need ### Do Not Introduce Without Explicit Approval - React - Vue - Angular - Blazor - .NET MAUI - Flutter - client-side SPA architecture - microservices - message brokers - CQRS frameworks - MediatR - generic repository abstractions merely for pattern compliance Use the simplest architecture that keeps domain rules testable and controllers thin. --- ## 3. Product Principles 1. **Server rendered first.** Razor owns primary page rendering. 2. **JavaScript enhances pages; it does not become the application framework.** jQuery and vanilla JavaScript are both approved. 3. **Mobile-first UX.** Grocery shopping is primarily performed from a phone. 4. **Retailer independence.** CartWise must remain useful with no retailer integration. 5. **Never fabricate freshness.** Every observed price has source and observation time. 6. **History is append-only where possible.** Do not overwrite price observations. 7. **UPC is an identifier, not the product primary key.** 8. **A grocery concept is different from a sellable product.** “Peanut butter” is a concept; “Jif Creamy 16 oz UPC X” is a product. 9. **Keep AI optional.** v1 does not need an LLM to function. 10. **Avoid premature complexity.** Start as a modular monolith. --- ## 4. Recommended Solution Structure ```text CartWise.sln src/ CartWise.Web/ Controllers/ ViewModels/ Views/ wwwroot/ css/ js/ pages/ services/ components/ utils/ images/ Program.cs appsettings.json CartWise.Application/ Interfaces/ Services/ DTOs/ Results/ Validation/ CartWise.Domain/ Entities/ Enums/ ValueObjects/ Rules/ CartWise.Infrastructure/ Data/ CartWiseDbContext.cs Configurations/ Migrations/ Integrations/ OpenFoodFacts/ USDA/ Retailers/ Services/ tests/ CartWise.Domain.Tests/ CartWise.Application.Tests/ CartWise.Web.Tests/ ``` ### Project Responsibilities #### CartWise.Web Owns: - MVC controllers - Razor views - ViewModels - model binding - anti-forgery handling - HTML/CSS/jQuery/vanilla JS - authentication UI - HTTP-specific behavior Must not contain pricing rules, replenishment rules, or database queries scattered through controllers. #### CartWise.Application Owns: - use-case orchestration - service interfaces - input validation that is not purely UI validation - application services - DTOs/results exchanged between Web and domain/infrastructure #### CartWise.Domain Owns: - core entities - enums - value objects - business invariants - price calculation rules - replenishment rules where they can be expressed independently of persistence Must not reference ASP.NET Core, EF Core, HTTP, external APIs, or Razor. #### CartWise.Infrastructure Owns: - EF Core DbContext - entity configurations - migrations - PostgreSQL-specific details - external product data providers - retailer adapters - persistence implementation --- ## 5. Core Domain Model ### 5.1 User / Household Use ASP.NET Core Identity for authentication unless the repository explicitly specifies another authentication system. #### ApplicationUser Extend IdentityUser only with fields that are truly needed. Suggested fields: - Id - DisplayName - CreatedUtc Do not duplicate Email, PasswordHash, etc. already supplied by Identity. #### Household ```text HouseholdId UUID PK Name varchar(120) not null CreatedUtc timestamptz not null CreatedByUserId FK ApplicationUser ``` #### HouseholdMember ```text HouseholdId UUID PK/FK UserId string PK/FK Role smallint not null JoinedUtc timestamptz not null ``` Roles: - Owner - Member Relationships: - Household 1..* HouseholdMembers - User 1..* HouseholdMembers Every household-scoped query must enforce membership authorization. --- ## 5.2 Grocery Concepts and Products ### GroceryConcept Represents what the shopper means generically. Examples: - Milk - Peanut Butter - Bananas - Ground Beef - Paper Towels Fields: ```text GroceryConceptId UUID PK Name varchar(160) not null NormalizedName varchar(160) not null CategoryId UUID nullable FK CreatedUtc timestamptz not null ``` Indexes: - unique/near-unique index on NormalizedName as appropriate - CategoryId ### Brand ```text BrandId UUID PK Name varchar(160) not null NormalizedName varchar(160) not null ``` ### ProductCategory Use a simple adjacency list if hierarchy is needed. ```text ProductCategoryId UUID PK Name varchar(160) not null ParentCategoryId UUID nullable FK ProductCategory ``` ### Product Represents a specific sellable/package-level product. ```text ProductId UUID PK GroceryConceptId UUID nullable FK BrandId UUID nullable FK Name varchar(240) not null NormalizedName varchar(240) not null SizeValue numeric(12,3) nullable SizeUnit varchar(32) nullable ImageUrl text nullable SourceType smallint not null SourceExternalId varchar(200) nullable CreatedUtc timestamptz not null UpdatedUtc timestamptz not null ``` SourceType examples: - Manual - OpenFoodFacts - USDA - Retailer ### ProductIdentifier Do not use UPC as Product PK. ```text ProductIdentifierId UUID PK ProductId UUID FK not null IdentifierType smallint not null Value varchar(64) not null NormalizedValue varchar(64) not null ``` IdentifierType: - UPC_A - UPC_E - EAN_8 - EAN_13 - GTIN - PLU - Other Indexes: - unique composite on IdentifierType + NormalizedValue - ProductId Relationship: - Product 1..* ProductIdentifiers --- ## 5.3 Household Product Preferences ### HouseholdProductPreference Maps a generic concept to household preferences. ```text HouseholdProductPreferenceId UUID PK HouseholdId UUID FK not null GroceryConceptId UUID FK not null PreferredProductId UUID nullable FK Product SubstitutionLevel smallint not null PreferredQuantity numeric(12,3) nullable PreferredUnit varchar(32) nullable UpdatedUtc timestamptz not null ``` SubstitutionLevel: 0 = ExactProductOnly 1 = SameBrand 2 = SimilarPreferredBrands 3 = AnyEquivalent 4 = CheapestAcceptable Unique index: - HouseholdId + GroceryConceptId --- ## 5.4 Shopping Lists ### ShoppingList ```text ShoppingListId UUID PK HouseholdId UUID FK not null Name varchar(160) not null Status smallint not null CreatedUtc timestamptz not null CompletedUtc timestamptz nullable CreatedByUserId FK ApplicationUser not null ``` Status: - Active - Completed - Archived v1 rule: a household should have at most one default active list unless multi-list functionality is explicitly added. ### ShoppingListItem ```text ShoppingListItemId UUID PK ShoppingListId UUID FK not null GroceryConceptId UUID nullable FK PreferredProductId UUID nullable FK DisplayName varchar(240) not null Quantity numeric(12,3) nullable Unit varchar(32) nullable Status smallint not null SortOrder int not null AddedByUserId FK ApplicationUser not null AddedUtc timestamptz not null PurchasedUtc timestamptz nullable Notes varchar(500) nullable RowVersion/version concurrency token ``` Status: - Needed - Purchased - Skipped - Unavailable - Substituted Rules: - DisplayName preserves what the user entered. - Concept/Product resolution can happen later. - Do not block list entry merely because a product cannot be resolved. --- ## 5.5 Retailers and Stores ### Retailer ```text RetailerId UUID PK Name varchar(160) not null NormalizedName varchar(160) not null WebsiteUrl text nullable ``` ### StoreLocation ```text StoreLocationId UUID PK RetailerId UUID FK not null ExternalStoreId varchar(120) nullable Name varchar(200) not null AddressLine1 varchar(200) nullable AddressLine2 varchar(200) nullable City varchar(120) nullable Region varchar(80) nullable PostalCode varchar(24) nullable CountryCode char(2) nullable Latitude numeric(9,6) nullable Longitude numeric(9,6) nullable CreatedUtc timestamptz not null UpdatedUtc timestamptz not null ``` Do not require PostGIS in first migration. Latitude/Longitude are enough initially. --- ## 5.6 Purchases and Price Observations ### Purchase Represents a shopping transaction. ```text PurchaseId UUID PK HouseholdId UUID FK not null StoreLocationId UUID nullable FK PurchasedByUserId FK ApplicationUser not null PurchasedUtc timestamptz not null Subtotal numeric(12,2) nullable Tax numeric(12,2) nullable Total numeric(12,2) nullable SourceType smallint not null CreatedUtc timestamptz not null ``` Purchase SourceType: - Manual - ShoppingMode - ReceiptImport - RetailerImport ### PurchaseItem ```text PurchaseItemId UUID PK PurchaseId UUID FK not null ProductId UUID nullable FK GroceryConceptId UUID nullable FK ShoppingListItemId UUID nullable FK Description varchar(240) not null Quantity numeric(12,3) not null default 1 Unit varchar(32) nullable LinePrice numeric(12,2) not null UnitPrice numeric(12,4) nullable CreatedUtc timestamptz not null ``` ### PriceObservation This is append-only history. ```text PriceObservationId UUID PK HouseholdId UUID nullable FK ProductId UUID not null FK StoreLocationId UUID nullable FK Price numeric(12,2) not null SalePrice numeric(12,2) nullable UnitPrice numeric(12,4) nullable CurrencyCode char(3) not null default 'USD' ObservedUtc timestamptz not null SourceType smallint not null SourceReference varchar(200) nullable ConfidenceScore numeric(5,4) not null CreatedUtc timestamptz not null ``` SourceType: - UserEntered - ShoppingMode - Receipt - OpenFoodFacts - RetailerApi - Instacart - ImportedPurchase Indexes: - ProductId + ObservedUtc DESC - ProductId + StoreLocationId + ObservedUtc DESC - HouseholdId + ProductId + ObservedUtc DESC Never update an old observation merely because a newer price appears. Add another row. --- ## 5.7 Running-Low / Replenishment Data Do not create an elaborate ML model in v1. A service can calculate replenishment dynamically from PurchaseItem history. If caching later becomes necessary, introduce a derived table. Suggested v1 calculation: 1. Gather the last N purchase dates for a household + concept/product. 2. Calculate intervals between purchases. 3. Use median interval rather than simple average when enough observations exist. 4. Require a minimum observation count before suggesting. 5. Calculate due date = last purchase + median interval. 6. Apply a configurable tolerance window. Never automatically add predicted items to a list in v1. Suggest them. --- ## 6. Entity Relationship Summary ```text ApplicationUser M:N Household through HouseholdMember Household 1:N ShoppingList 1:N Purchase 1:N HouseholdProductPreference ShoppingList 1:N ShoppingListItem GroceryConcept 1:N Product 1:N ShoppingListItem 1:N HouseholdProductPreference Brand 1:N Product Product 1:N ProductIdentifier 1:N PurchaseItem 1:N PriceObservation Retailer 1:N StoreLocation StoreLocation 1:N Purchase 1:N PriceObservation Purchase 1:N PurchaseItem ``` Avoid cascade delete where deletion would destroy historical purchase or price data. Prefer restrict/no-action for historical relationships. --- ## 7. DbContext and EF Core Rules Use explicit IEntityTypeConfiguration classes in `Infrastructure/Data/Configurations`. Examples: ```text HouseholdConfiguration.cs ShoppingListConfiguration.cs ShoppingListItemConfiguration.cs ProductConfiguration.cs ProductIdentifierConfiguration.cs PurchaseConfiguration.cs PurchaseItemConfiguration.cs PriceObservationConfiguration.cs ``` Guidelines: - PostgreSQL UUIDs for domain IDs. - Store money as decimal/numeric, never float/double. - Use timestamptz/UTC timestamps. - Normalize searchable product/brand/concept names. - Use indexes intentionally. - Use optimistic concurrency on ShoppingListItem to reduce shared-list lost updates. - Do not use lazy loading. - Use `AsNoTracking()` for read-only queries. - Avoid returning EF entities directly to Razor views. --- ## 8. Application Services Create focused services. Avoid “God services”. ### IHouseholdService / HouseholdService Responsibilities: - create household - add/remove member - verify access - retrieve current household ### IShoppingListService / ShoppingListService Responsibilities: - get active list - create list - add item - mark purchased - skip/unskip - delete item - reorder if implemented - resolve item to concept/product ### IProductService / ProductService Responsibilities: - search local catalog - get product details - resolve barcode - create manually entered product - call product enrichment providers when local lookup misses ### IProductEnrichmentService Coordinates external data without exposing provider details to controllers. ### IPriceService / PriceService Responsibilities: - record price observation - get household price history - calculate latest observed price - calculate median/average household paid price - calculate lowest recent observed price - calculate unit price when package quantity is known - report freshness/source/confidence ### IPurchaseService / PurchaseService Responsibilities: - start purchase - add purchase item - complete purchase - derive PriceObservation from recorded PurchaseItem - link purchased item to ShoppingListItem ### IReplenishmentService / ReplenishmentService Responsibilities: - calculate likely-running-low concepts/products - use transparent deterministic statistics - return score/reason/due estimate ### IShoppingModeService / ShoppingModeService Responsibilities: - prepare shopping-mode view - group/sort items - record purchase/skip/unavailable/substitution transitions ### IStoreService / StoreService Responsibilities: - CRUD/search store locations used by household - retailer/store resolution --- ## 9. External Product Data Abstraction Define: ```csharp public interface IProductDataProvider { string Name { get; } Task FindByBarcodeAsync( string barcode, CancellationToken cancellationToken = default); Task> SearchAsync( string query, CancellationToken cancellationToken = default); } ``` Potential implementations: - `OpenFoodFactsProductDataProvider` - `UsdaProductDataProvider` Provider data must be normalized into CartWise domain/application DTOs before persistence. Never bind Razor pages directly to provider-specific response models. ### Lookup Flow ```text Barcode received -> normalize barcode -> local ProductIdentifier query -> if found, return local product -> if missing, query configured provider(s) -> normalize result -> persist Product + ProductIdentifier if acceptable -> return product ``` Respect provider rate limits. Cache imported products locally. --- ## 10. Retailer Adapter Contract — Future-Compatible v1 Retailer integrations are not required for initial launch, but design an interface so they can be added later. ```csharp [Flags] public enum RetailerCapabilities { None = 0, StoreSearch = 1, ProductSearch = 2, Pricing = 4, Inventory = 8, Promotions = 16, AisleLocation = 32 } public interface IRetailerProvider { string Name { get; } RetailerCapabilities Capabilities { get; } } ``` Do not fill v1 with fake methods that no provider can support yet. Add capability-specific interfaces when implementing real integrations. Never write controller/domain logic such as `if (retailer.Name == "Meijer")`. --- ## 11. MVC Controllers and Responsibilities Controllers must be thin and delegate to services. ### HomeController Routes: ```text GET / GET /Home/Index ``` Responsibilities: - dashboard - active list summary - running-low suggestions - recent price highlights ### HouseholdController Routes: ```text GET /household GET /household/create POST /household/create GET /household/members POST /household/members/invite [future/simple v1 if desired] POST /household/members/remove ``` ### ShoppingListController Prefer clean route attributes. Routes: ```text GET /list POST /list/items POST /list/items/{id}/toggle POST /list/items/{id}/skip POST /list/items/{id}/delete POST /list/items/{id}/resolve ``` For progressive enhancement: - standard form posts must work where practical - JS-enhanced endpoints may return partial HTML or JSON - anti-forgery is mandatory on state-changing requests ### ProductController Routes: ```text GET /products/{id} GET /products/search?q=milk GET /products/barcode/{barcode} ``` If live search needs JSON: ```text GET /api/products/search?q=milk GET /api/products/barcode/{barcode} ``` Only introduce `/api` endpoints for actual client-side needs. ### ShoppingController Routes: ```text GET /shopping/start POST /shopping/items/{id}/purchase POST /shopping/items/{id}/skip POST /shopping/items/{id}/unavailable POST /shopping/items/{id}/substitute POST /shopping/complete ``` ### PriceController Routes: ```text GET /prices GET /prices/product/{productId} POST /prices/product/{productId}/observe ``` ### PurchaseController Routes: ```text GET /purchases GET /purchases/{id} ``` Manual purchase edit screens can be added if needed but are not core first-pass scope. ### ScanController Routes: ```text GET /scan ``` The scan page uses camera/barcode JS and requests product resolution through the Product endpoint. --- ## 12. ViewModels Never pass EF Core entities directly to views. ### HomeViewModel ```text HouseholdName ActiveListItemCount RunningLowSuggestions[] RecentPriceInsights[] ``` ### ShoppingListViewModel ```text ShoppingListId Name Items[] RunningLowSuggestions[] ``` ### ShoppingListItemViewModel ```text Id DisplayName QuantityDisplay Status ProductId? ProductImageUrl? IsResolved PriceHint? ConcurrencyToken ``` ### ProductDetailsViewModel ```text ProductId Name BrandName ConceptName PackageDisplay BarcodeIdentifiers[] ImageUrl LatestPrice? AverageHouseholdPrice? LowestHouseholdPrice? UnitPrice? PriceHistory[] ``` ### ShoppingModeViewModel ```text ShoppingListId StoreLocation? RemainingItemCount PurchasedItemCount Groups[] ``` ### ShoppingModeGroupViewModel ```text Name SortOrder Items[] ``` Group names in v1 may be category-based rather than real store aisles: - Produce - Meat - Dairy - Pantry - Frozen - Household - Other ### PriceHistoryViewModel ```text ProductId ProductName Observations[] AveragePaid MedianPaid LowestPaid LatestPaid ``` ### RunningLowSuggestionViewModel ```text GroceryConceptId Name LastPurchasedUtc TypicalIntervalDays EstimatedDueUtc Confidence Reason ``` --- ## 13. Razor View Structure ```text Views/ Shared/ _Layout.cshtml _MobileBottomNav.cshtml _ValidationScriptsPartial.cshtml _ProductImage.cshtml Error.cshtml Home/ Index.cshtml Household/ Index.cshtml Create.cshtml Members.cshtml ShoppingList/ Index.cshtml _ListItem.cshtml _RunningLowSuggestion.cshtml Product/ Details.cshtml Search.cshtml _SearchResults.cshtml Shopping/ Start.cshtml _ShoppingItem.cshtml Price/ Index.cshtml Product.cshtml Purchase/ Index.cshtml Details.cshtml Scan/ Index.cshtml ``` Use partials for server-rendered fragments that JavaScript may refresh. --- ## 14. jQuery and Vanilla JavaScript Organization ```text wwwroot/js/ pages/ shopping-list.js shopping-mode.js product-search.js scan.js price-history.js services/ http.js barcode.js offline.js # later components/ toast.js dialog.js utils/ dom.js money.js dates.js ``` Rules: - jQuery and vanilla JavaScript are both approved frontend tools - prefer jQuery for straightforward DOM selection/manipulation, delegated events, form serialization, AJAX calls, and partial-page updates when it makes the code shorter and clearer - prefer vanilla JavaScript for browser-native APIs such as `navigator.mediaDevices`, Service Workers, IndexedDB, Web APIs, and small focused logic where jQuery provides no benefit - use `$.ajax`, `$.get`, or `$.post` for jQuery-oriented asynchronous requests; `fetch` is also allowed when native code is clearer - ES modules are allowed for larger or isolated frontend features, but do not force every small script into a module - do not mix jQuery and native DOM code arbitrarily within the same function; choose the clearest style for that unit of work - no giant global `app.js` - no framework-like custom abstraction layer - do not build a homemade SPA or component framework - preserve non-JS fallback where reasonable - send ASP.NET Core anti-forgery tokens on all state-changing AJAX/fetch requests - keep selectors stable by using semantic IDs, classes, and `data-*` attributes rather than brittle DOM traversal --- ## 15. Primary Page Designs for v1 ### A. Home Show: - active list count - quick add - likely-running-low suggestions - recent “good price”/price-history insights only if enough data exists ### B. Smart Grocery List Show: - quick add input - unresolved text items allowed - resolved product indication - check/skip/delete - household changes reflected after reload; near-real-time sync can come later ### C. Product Details / Price Intelligence Show: - product image/name/brand/size - identifiers - household normal price - lowest observed price - latest observed price - price history - source/freshness on observations ### D. Scan Show: - camera permission request - barcode scanner - resolved product result - Add to List - Record Purchase / Record Price ### E. Shopping Mode Show: - large touch targets - remaining count - grouped list - purchase action - optional price entry - skip/unavailable - minimum visual noise ### F. Prices Show: - products the household has purchased - latest / average / low price - sort/filter/search ### G. Household Show: - members - household name - product preferences - preferred stores later --- ## 16. Price Intelligence Rules Implement deterministic functions first. ### Unit Price If price and package size are known: ```text unitPrice = effectivePrice / packageSize ``` Keep unit labels. Do not compare incompatible units without an explicit conversion service. ### Household Average Price Use purchase/observed history for the household and product. Prefer both: - arithmetic average for reporting - median for “typical” price because grocery promotions cause outliers ### Good Price v1 Only display if minimum history threshold is met. Example: ```text if current <= 10th/20th percentile or materially below median label = Good Price ``` Do not hardcode an arbitrary universal threshold without tests. ### Freshness Display: - observation date - source type - confidence if useful Do not display “current price” for stale user observations. Prefer “last observed”. --- ## 17. Security and Authorization Mandatory: - ASP.NET Core Identity - authorization checks on every household-scoped resource - anti-forgery tokens on state changes - server-side validation - encode all user-entered output through Razor defaults - do not trust ProductId/HouseholdId sent by client - prevent IDOR by checking household membership - secure external API keys in configuration/user secrets/environment variables - never place provider secrets in JS - rate limit public search/scan endpoints if exposed anonymously Use policies or a centralized household authorization service rather than repeating fragile checks. --- ## 18. Validation Rules Examples: - Shopping item display name: required, max 240 - Quantity: positive when supplied - Money: non-negative, realistic maximum - Barcode: normalize digits; validate supported length/check digit when practical - Household name: required, max 120 - Product names: required when manual product created - Price observation must have ProductId + price + observation date/source Return user-friendly validation messages. Log technical details server-side. --- ## 19. Logging / Diagnostics Use `ILogger` structured logging. Log: - external provider failures - unresolved barcodes - product import/enrichment outcomes - unexpected price calculation errors - authorization failures without sensitive detail Do not log: - passwords - auth tokens - complete sensitive user payloads unnecessarily Include correlation/request IDs through normal ASP.NET logging. --- ## 20. Testing Strategy ### Domain Tests Test: - unit price calculations - price statistics - replenishment interval calculations - substitution enum/rules if logic exists ### Application Tests Test: - add list item - resolve barcode local hit/miss - purchase creates price observation - household access enforcement - running-low service ### Web / Integration Tests Use WebApplicationFactory where useful. Test: - unauthenticated redirects - anti-forgery behavior - household resource isolation - key MVC routes - form validation Do not spend early effort on pixel-perfect browser automation before domain workflows stabilize. --- ## 21. Seed / Development Data Create development-only seed data: - one test household - 10–20 grocery concepts - several brands/products - identifiers - sample store locations - 60–90 days of purchase/price history Never seed production with fake price data. Suggested concepts: - Milk - Eggs - Bread - Bananas - Chicken Breast - Ground Beef - Peanut Butter - Coffee - Cereal - Cheddar Cheese --- ## 22. Exact Implementation Order Agents must follow this order unless a blocking dependency requires a small adjustment. ### 22A. Current MVP-Now Build Order For the current local MVP target, execute in this order unless the user explicitly reprioritizes: 1. Foundation: solution, SQLite wiring, Identity, baseline tests. 2. Household: household entities, authorization, create household flow. 3. Grocery List: list and list item workflows with mobile-first Razor UI. 4. Purchases and Price History: manual purchase entry and basic price history. 5. Running-Low: deterministic suggestions on home/list experiences. 6. MVP Hardening: authorization review, anti-forgery review, mobile usability pass, release checklist verification. Post-MVP resumes after that in the backlog-defined order, especially product catalog/barcode work and dedicated shopping mode. ### Phase 0 — Repository and Baseline 1. Create `CartWise.sln` and the four projects. 2. Add project references with clean direction: - Web -> Application - Web -> Infrastructure only for startup registration if necessary - Application -> Domain - Infrastructure -> Application + Domain 3. Configure nullable reference types. 4. Configure formatting/analyzers. 5. Configure PostgreSQL connection through environment/user secrets. - For the current MVP, SQLite may be used instead and is preferred for local delivery speed. 6. Add ASP.NET Core Identity. 7. Verify app starts and basic test project runs. **Exit condition:** authenticated MVC shell runs against PostgreSQL. For the current MVP, an authenticated MVC shell running correctly against SQLite is sufficient. ### Phase 1 — Household Foundation 1. ApplicationUser. 2. Household. 3. HouseholdMember. 4. EF configurations/migration. 5. Household service. 6. Household authorization helper/policy. 7. Household create/index UI. 8. Tests. **Exit condition:** a user can create a household and all household data is access-controlled. ### Phase 2 — Smart Shopping List 1. GroceryConcept. 2. ProductCategory. 3. ShoppingList. 4. ShoppingListItem. 5. migrations/configurations. 6. ShoppingListService. 7. ShoppingListController. 8. list Razor view and partial. 9. add/toggle/delete functionality. 10. minimal jQuery/vanilla JS progressive enhancement. 11. concurrency handling. 12. tests. **Exit condition:** household users can maintain a useful shared grocery list even with zero product catalog data. ### Phase 3 — Product Catalog and Barcode Resolution 1. Brand. 2. Product. 3. ProductIdentifier. 4. HouseholdProductPreference. 5. database migration/indexes. 6. ProductService. 7. `IProductDataProvider`. 8. implement Open Food Facts provider first. 9. local-first barcode lookup. 10. ProductController routes. 11. product details/search views. 12. Scan page and barcode JS integration. 13. tests with provider mocked. **Exit condition:** scan known barcode -> local or external lookup -> product persisted -> viewable/addable to list. ### Phase 4 — Stores, Purchases, and Price History 1. Retailer. 2. StoreLocation. 3. Purchase. 4. PurchaseItem. 5. PriceObservation. 6. migration/indexes. 7. StoreService. 8. PurchaseService. 9. PriceService. 10. guarantee a purchase item can create a price observation. 11. PriceController + price views. 12. Product details price-history section. 13. tests. **Exit condition:** user records a purchase price and later sees accurate personal price history. ### Phase 5 — Shopping Mode 1. ShoppingModeViewModel. 2. ShoppingModeService. 3. category grouping. 4. mobile-first shopping view. 5. purchase/skip/unavailable actions. 6. optional price entry per purchase. 7. purchase completion flow. 8. tests. **Exit condition:** a shopper can use the app comfortably during a real grocery trip. ### Phase 6 — Running-Low Suggestions 1. Replenishment service. 2. median interval algorithm. 3. confidence/minimum-history rules. 4. Home and List suggestion partials. 5. “Add suggestion to list” action. 6. tests against known purchase histories. **Exit condition:** app can suggest likely-needed repeat items without ML or automatic list mutation. ### Phase 7 — Polish / PWA Preparation 1. accessibility pass. 2. responsive/mobile testing. 3. performance profiling. 4. service worker/manifest only if explicitly included in v1 release. 5. IndexedDB/offline queue only after core workflows are stable. 6. production diagnostics and health checks. --- ## 23. Definition of Done for v1 ### 23A. Definition of Done for the Current MVP The current MVP is done when an authenticated household user can: 1. Register and sign in. 2. Create and use a household. 3. Maintain a grocery list from a phone-sized layout. 4. Add free-text grocery concepts without requiring catalog resolution. 5. Toggle, skip, edit, or delete list items as required by the MVP backlog. 6. Record purchase prices manually. 7. See basic personal price history. 8. Receive deterministic running-low suggestions from purchase history. 9. Use the application locally with SQLite and without retailer integrations, barcode scanning, or external product providers. For the current MVP, use `docs/release-mvp-checklist.md` as the practical release gate. CartWise v1 is done when an authenticated household can: 1. Create/use a household. 2. Maintain a grocery list from a phone. 3. Add free-text grocery concepts without requiring catalog resolution. 4. Resolve and scan UPC/GTIN products. 5. Save product preferences. 6. Enter shopping mode. 7. Mark items purchased/skipped/unavailable. 8. Record purchase prices. 9. See personal price history with source/freshness. 10. See unit prices when enough data exists. 11. Receive deterministic running-low suggestions from purchase history. 12. Use the application without any retailer API integration. --- ## 24. Explicitly Out of Scope for v1 Unless the user explicitly changes scope, do not build: - grocery delivery - checkout/payment processing - retailer loyalty login - coupon clipping - live universal inventory - universal retailer price scraping - meal planning - recipe generation - nutrition optimization - AI chatbot - multi-store route optimization - indoor store maps - aisle crowdsourcing - receipt OCR - shrinkflation alerts - push notifications - social features Architectural seams may be left for later, but do not build speculative systems. --- ## 25. Agent Coding Rules 1. Read this file before changing architecture. 2. Review `docs/scrum-backlog.md` before starting implementation work. 3. Update `docs/scrum-backlog.md` as work starts, changes, and completes. 4. Review `docs/decision-log.md` before changing near-term MVP scope or delivery assumptions. 5. Inspect existing code before creating parallel abstractions. 6. Prefer extending existing patterns over inventing new ones. 7. Keep controllers thin. 8. Keep persistence code out of Razor views and controllers. 9. Keep external provider response types inside Infrastructure. 10. Add tests for business rules and bug fixes. 11. Run build/tests after meaningful changes. 12. Do not silently change database semantics. 13. Create migrations for schema changes; never hand-edit production schema. 14. Never fabricate retailer capabilities or data. 14. If external data is unavailable, represent it as unavailable/stale—not guessed. 16. Preserve backward-compatible URLs where practical once routes ship. 17. Use comments for why, not obvious what. 18. Prefer clear conventional C# over clever abstraction. --- ## 26. Naming Conventions - Entities: singular nouns (`Product`, `PurchaseItem`). - Controllers: `Controller`. - Services: `IService` + `Service`. - ViewModels: page/use-case specific (`ShoppingModeViewModel`). - Async methods end with `Async`. - UTC timestamp properties end in `Utc`. - IDs use `NameId` except Identity's standard `Id` where appropriate. - JavaScript files use kebab-case. - Razor partials begin with `_`. --- ## 27. First Vertical Slice If an agent is asked to “start building CartWise,” do not scaffold every feature simultaneously. Build this first vertical slice end-to-end: ```text Register/Login -> Create Household -> Open Active Grocery List -> Add “Milk” -> Toggle Purchased -> Reload and verify persisted state ``` Then extend it in the implementation order above. --- ## 28. Product North Star When making tradeoffs, remember: > CartWise is the shopper's grocery memory. The first release should make it effortless to remember **what the household needs, what it buys, and what it normally pays**. Retailer integrations are enhancements, not foundations.