This repository builds CartWise, a mobile-first grocery companion implemented as a conventional ASP.NET Core MVC application.
The canonical architecture and scope are defined in AGENTS.md. Read AGENTS.md completely before making architectural changes. This file gives Claude-specific operating guidance and a compact execution map.
Use:
Do not introduce React, Vue, Angular, Blazor, MAUI, Flutter, SPA architecture, microservices, MediatR, or unnecessary repository/unit-of-work wrappers unless explicitly requested.
CartWise v1 must help a household:
CartWise must still be useful with zero retailer API access.
CartWise.Web
CartWise.Application
CartWise.Domain
No EF Core, HTTP, Razor, or provider SDK dependencies here.
CartWise.Infrastructure
Dependency direction should remain clean.
Never collapse these concepts:
GroceryConcept: Peanut Butter
Product: Jif Creamy Peanut Butter 16 oz
ProductIdentifier: UPC/GTIN identifying that package
A shopping list item may exist with only free text / concept data. Product resolution must not be mandatory to make a useful list.
UPC/GTIN is never the Product primary key.
Implement in the order given in AGENTS.md.
See AGENTS.md for exact fields, indexes, enums, and relationships.
Keep controllers thin and use:
IHouseholdServiceIShoppingListServiceIProductServiceIProductEnrichmentServiceIPriceServiceIPurchaseServiceIReplenishmentServiceIShoppingModeServiceIStoreServiceDo not create one giant CartWiseService.
Do not add abstractions without a real responsibility or testability benefit.
External provider models stay inside Infrastructure.
Use a normalized contract similar to:
public interface IProductDataProvider
{
string Name { get; }
Task<ProductLookupResult?> FindByBarcodeAsync(
string barcode,
CancellationToken cancellationToken = default);
Task<IReadOnlyList<ProductSearchResult>> SearchAsync(
string query,
CancellationToken cancellationToken = default);
}
Lookup is always local first, provider second, then normalize and cache.
Open Food Facts should be the first packaged-product provider. USDA can enrich generic/nutrition data later.
Never expose an external provider's API response object to a controller or Razor view.
Retailer integrations are optional.
Do not build application logic around specific retailer names. Use capability-aware adapters.
Never assume that price, inventory, promotions, or aisle data are available.
When data is stale or unavailable, show that state explicitly instead of guessing.
Preserve these route intentions unless existing code requires a compatible adjustment.
GET / Home
GET /household Household
GET /list Active grocery list
POST /list/items Add list item
POST /list/items/{id}/toggle Toggle purchased
POST /list/items/{id}/skip
POST /list/items/{id}/delete
GET /products/{id}
GET /products/search?q=
GET /products/barcode/{barcode}
GET /scan
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
GET /prices
GET /prices/product/{productId}
POST /prices/product/{productId}/observe
GET /purchases
GET /purchases/{id}
Use /api/... only where browser JavaScript genuinely benefits from JSON.
Razor is primary rendering. jQuery and vanilla JavaScript are both approved.
Prefer jQuery for:
Prefer vanilla JavaScript for:
navigator.mediaDevicesOrganize JavaScript under:
wwwroot/js/pages/
wwwroot/js/services/
wwwroot/js/components/
wwwroot/js/utils/
Do not create a global monolithic app.js.
Do not reinvent client-side framework patterns by hand.
Do not mix jQuery and native DOM approaches arbitrarily inside the same function.
Use $.ajax/$.get/$.post where jQuery is the clearer choice and fetch where native code is more appropriate.
Use anti-forgery tokens for every state-changing JavaScript request.
AsNoTracking() for read paths.PriceObservation is append-only.AGENTS.md.Every schema change requires an EF Core migration.
Do not call a stale observation a current price.
Every price observation includes:
Display source/freshness where material.
For a household's typical price, favor median as the robust statistic; average may also be shown.
Only label a price “good” after enough history exists.
Unit price comparisons must use compatible units.
No ML or LLM required.
Use purchase history:
Never automatically add predicted groceries to the list. The user chooses.
Household isolation is critical.
For every household-scoped resource:
Use centralized household authorization/service logic.
All state-changing MVC requests require anti-forgery protection.
Before modifying code:
AGENTS.md.While implementing:
CancellationToken through external/database-heavy application paths where appropriate.After implementing:
Do not claim a feature works without running the relevant build/tests when the environment permits it.
Follow this progression:
Do not jump to Phase 6 features while Phase 2 is incomplete unless the user explicitly requests it.
When asked to begin implementation, build this before broad scaffolding:
Register/Login
-> Create Household
-> Open /list
-> Add “Milk”
-> Persist item
-> Toggle Purchased
-> Reload
-> Verify correct household-scoped state
This proves authentication, household authorization, EF persistence, MVC routing, Razor rendering, and form/JS behavior.
Do not implement proactively:
Leave clean seams for future work; do not build speculative infrastructure.
A good change:
North star:
CartWise remembers what the household needs, what it buys, and what it normally pays.
Powered by TurnKey Linux.