Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

34KB

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.

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

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.

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

HouseholdId UUID PK
Name varchar(120) not null
CreatedUtc timestamptz not null
CreatedByUserId FK ApplicationUser

HouseholdMember

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:

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

BrandId UUID PK
Name varchar(160) not null
NormalizedName varchar(160) not null

ProductCategory

Use a simple adjacency list if hierarchy is needed.

ProductCategoryId UUID PK
Name varchar(160) not null
ParentCategoryId UUID nullable FK ProductCategory

Product

Represents a specific sellable/package-level product.

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.

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.

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

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

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

RetailerId UUID PK
Name varchar(160) not null
NormalizedName varchar(160) not null
WebsiteUrl text nullable

StoreLocation

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.

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

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.

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

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:

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:

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

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.

[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:

GET /
GET /Home/Index

Responsibilities:

  • dashboard
  • active list summary
  • running-low suggestions
  • recent price highlights

HouseholdController

Routes:

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:

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:

GET /products/{id}
GET /products/search?q=milk
GET /products/barcode/{barcode}

If live search needs JSON:

GET /api/products/search?q=milk
GET /api/products/barcode/{barcode}

Only introduce /api endpoints for actual client-side needs.

ShoppingController

Routes:

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:

GET  /prices
GET  /prices/product/{productId}
POST /prices/product/{productId}/observe

PurchaseController

Routes:

GET /purchases
GET /purchases/{id}

Manual purchase edit screens can be added if needed but are not core first-pass scope.

ScanController

Routes:

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

HouseholdName
ActiveListItemCount
RunningLowSuggestions[]
RecentPriceInsights[]

ShoppingListViewModel

ShoppingListId
Name
Items[]
RunningLowSuggestions[]

ShoppingListItemViewModel

Id
DisplayName
QuantityDisplay
Status
ProductId?
ProductImageUrl?
IsResolved
PriceHint?
ConcurrencyToken

ProductDetailsViewModel

ProductId
Name
BrandName
ConceptName
PackageDisplay
BarcodeIdentifiers[]
ImageUrl
LatestPrice?
AverageHouseholdPrice?
LowestHouseholdPrice?
UnitPrice?
PriceHistory[]

ShoppingModeViewModel

ShoppingListId
StoreLocation?
RemainingItemCount
PurchasedItemCount
Groups[]

ShoppingModeGroupViewModel

Name
SortOrder
Items[]

Group names in v1 may be category-based rather than real store aisles:

  • Produce
  • Meat
  • Dairy
  • Pantry
  • Frozen
  • Household
  • Other

PriceHistoryViewModel

ProductId
ProductName
Observations[]
AveragePaid
MedianPaid
LowestPaid
LatestPaid

RunningLowSuggestionViewModel

GroceryConceptId
Name
LastPurchasedUtc
TypicalIntervalDays
EstimatedDueUtc
Confidence
Reason

13. Razor View Structure

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

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:

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:

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.

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.
  6. Add ASP.NET Core Identity.
  7. Verify app starts and basic test project runs.

Exit condition: authenticated MVC shell runs against PostgreSQL.

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

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. Inspect existing code before creating parallel abstractions.
  5. Prefer extending existing patterns over inventing new ones.
  6. Keep controllers thin.
  7. Keep persistence code out of Razor views and controllers.
  8. Keep external provider response types inside Infrastructure.
  9. Add tests for business rules and bug fixes.
  10. Run build/tests after meaningful changes.
  11. Do not silently change database semantics.
  12. Create migrations for schema changes; never hand-edit production schema.
  13. Never fabricate retailer capabilities or data.
  14. If external data is unavailable, represent it as unavailable/stale—not guessed.
  15. Preserve backward-compatible URLs where practical once routes ship.
  16. Use comments for why, not obvious what.
  17. 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:

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.

Powered by TurnKey Linux.