# 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
- `docs/prompt-process.md` for the standard prompt format and prompt-repair workflow

## 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.

If a substantial user prompt does not follow the standard structure in `docs/prompt-process.md`, agents should still help, but should also briefly show a corrected prompt in the recommended format and then proceed using that repaired structure.

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<T> 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<ProductLookupResult?> FindByBarcodeAsync(
        string barcode,
        CancellationToken cancellationToken = default);

    Task<IReadOnlyList<ProductSearchResult>> 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<T>` 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. Use `docs/prompt-process.md` for substantial prompts and show repaired prompt examples when a user request is too informal for reliable execution.
6. Inspect existing code before creating parallel abstractions.
7. Prefer extending existing patterns over inventing new ones.
8. Keep controllers thin.
9. Keep persistence code out of Razor views and controllers.
10. Keep external provider response types inside Infrastructure.
11. Add tests for business rules and bug fixes.
12. Run build/tests after meaningful changes.
13. Do not silently change database semantics.
14. Create migrations for schema changes; never hand-edit production schema.
15. Never fabricate retailer capabilities or data.
14. If external data is unavailable, represent it as unavailable/stale—not guessed.
17. Preserve backward-compatible URLs where practical once routes ship.
18. Use comments for why, not obvious what.
19. Prefer clear conventional C# over clever abstraction.

---

## 26. Naming Conventions

- Entities: singular nouns (`Product`, `PurchaseItem`).
- Controllers: `<Resource>Controller`.
- Services: `I<Resource>Service` + `<Resource>Service`.
- ViewModels: page/use-case specific (`ShoppingModeViewModel`).
- Async methods end with `Async`.
- UTC timestamp properties end in `Utc`.
- IDs use `<Entity>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.
