# CartWise Scrum Backlog

## Purpose

This document translates the product and implementation guidance from `AGENTS.md` into a Scrum-friendly backlog for CartWise v1. It organizes work into epics, user stories, and implementation tasks that align with the required phased delivery order.

This backlog is optimized for a solo founder + AI delivery model targeting a local MVP by **September 10, 2026**.

## Product Vision

CartWise is a mobile-first grocery companion web app that helps households remember what to buy, what they bought, what they paid, and what they may need again soon.

## Scrum Workflow

### Solo Cadence

- Planning cadence: weekly
- Delivery rhythm: one active story at a time
- Daily check-in: review top 3 priorities and blockers
- Backlog refinement: once per week
- Review/demo: every Friday
- Retrospective: every 2 weeks

### Working Model

- **Founder / Product Owner**: prioritizes scope, clarifies acceptance criteria, reviews AI output, and decides trade-offs
- **AI Development Partner**: scaffolds, implements, documents, and proposes follow-up tasks within approved scope
- **Primary QA**: founder validates behavior locally before marking stories done

### Board Columns

- Backlog
- Ready
- In Sprint
- In Progress
- Code Review
- Verify
- Done

### Estimation

- Use `XS`, `S`, `M`, and `L` sizing on stories
- Split any story larger than `L` before implementation
- Track tasks as checklist items and keep them aligned with the repository state

## Definitions

### Definition of Ready

A story is ready when:

- Business value is clear
- Acceptance criteria are written
- Dependencies are identified
- UI, domain, and data impact are understood
- Test approach is known
- The story is small enough to deliver in a sprint

### Definition of Done

A story is done when:

- Code is implemented
- Tests are added or updated
- Build passes
- Authorization and validation are enforced where applicable
- Logging is added where useful
- User-facing behavior is verified
- Work remains aligned with `AGENTS.md` architecture and scope

## Tracking Conventions

- Use `- [ ]` for not started tasks
- Use `- [x]` for completed tasks
- Update story acceptance criteria or add a short status note when a story is fully delivered
- Keep task state aligned with the actual repository state

## Release Strategy

- `Release: MVP` means required for the local launch target by **September 10, 2026**
- `Release: Post-MVP` means explicitly deferred until after MVP is stable
- `Release: Deferred` means do not implement unless scope changes
- `Priority: P0` means must ship for MVP
- `Priority: P1` means valuable next but can slip
- `Priority: P2` means later or exploratory
- `Effort: XS` under 2 hours, `S` about half a day, `M` about a day, `L` 2 or more days
- `Decision Gate` calls out places where the founder must make a product or architecture decision before implementation continues

## Epic Roadmap

### MVP Release
1. `CW-EPIC-01` Platform Foundation
2. `CW-EPIC-02` Household and Access Control
3. `CW-EPIC-03` Smart Shopping List
4. `CW-EPIC-05` Stores, Purchases, and Price Intelligence
5. `CW-EPIC-07` Running-Low Suggestions
6. Selected hardening work from `CW-EPIC-08`

### Post-MVP Release
1. `CW-EPIC-04` Product Catalog and Barcode Resolution
2. `CW-EPIC-06` Shopping Mode
3. Remaining polish work from `CW-EPIC-08`

---

## `CW-EPIC-01` Platform Foundation

**Goal:** establish the solution structure, authentication, SQLite wiring for MVP, and baseline engineering standards.

### `CW-STORY-01.1` Create solution skeleton
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a developer, I want the CartWise solution scaffolded so the app follows a clean modular architecture.

**Acceptance criteria**
- `CartWise.sln` exists
- `src/` and `tests/` project layout matches the spec
- Project references follow approved dependency direction
- Nullable reference types are enabled

**Tasks**
- [x] Create `CartWise.sln`
- [x] Create `src/CartWise.Web`
- [x] Create `src/CartWise.Application`
- [x] Create `src/CartWise.Domain`
- [x] Create `src/CartWise.Infrastructure`
- [x] Create `tests/CartWise.Domain.Tests`
- [x] Create `tests/CartWise.Application.Tests`
- [x] Create `tests/CartWise.Web.Tests`
- [x] Add project references
- [x] Enable nullable reference types

**Status:** Done — solution builds and baseline template tests pass across all three test projects. `CartWise.Web` references `CartWise.Infrastructure` in addition to `CartWise.Application` for DI/startup registration (allowed per `AGENTS.md` §22 Phase 0).

### `CW-STORY-01.2` Configure application startup
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a developer, I want a runnable MVC shell so the team can build on a working baseline.

**Acceptance criteria**
- ASP.NET Core MVC app starts locally
- Shared layout and static assets load correctly
- Environment-based configuration is wired
- SQLite connection is configurable for MVP
- PostgreSQL migration path remains possible later

**Tasks**
- [x] Configure MVC services and middleware
- [x] Add base layout and navigation shell
- [x] Organize `wwwroot` structure
- [x] Add configuration bindings
- [x] Wire SQLite connection string for MVP
- [x] Note PostgreSQL transition path in docs
- [x] Verify local app startup

**Status:** Done — app starts and serves the rebranded layout and static assets on a local port (verified via `dotnet run` + curl smoke check). `CartWiseDbContext` (empty for now — entities land in `CW-EPIC-02`) is wired to SQLite through `CartWise.Infrastructure.InfrastructureServiceCollectionExtensions.AddInfrastructure`, using `ConnectionStrings:DefaultConnection` in `appsettings.json`. PostgreSQL transition path documented in `docs/decision-log.md` under `DEC-005`.

### `CW-STORY-01.3` Add Identity authentication
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a user, I want to sign in so my household data is protected.

**Acceptance criteria**
- Registration and sign-in are available
- Authenticated routes can be protected
- Identity persistence is configured

**Tasks**
- [x] Add ASP.NET Core Identity
- [x] Create `ApplicationUser`
- [x] Configure Identity persistence
- [x] Scaffold auth UI
- [x] Verify login and logout flow

**Status:** Done — `ApplicationUser : IdentityUser` (`DisplayName`, `CreatedUtc`) lives in `CartWise.Infrastructure.Identity`; `CartWiseDbContext` is now `IdentityDbContext<ApplicationUser>`. Identity is registered via `AddInfrastructure` (`AddIdentity` + `AddEntityFrameworkStores<CartWiseDbContext>`). Auth UI is a conventional MVC `AccountController` (`Register`/`Login`/`Logout`) with matching Razor views under `Views/Account`, anti-forgery on all POSTs, open-redirect guarded via `Url.IsLocalUrl`. `HomeController.Privacy` carries `[Authorize]` as the protected-route proof. Initial `InitialIdentity` EF Core migration created in `CartWise.Infrastructure/Data/Migrations`; the Development environment auto-applies pending migrations on startup (`Program.cs`) so local setup needs no manual `dotnet ef database update` step. Verified end-to-end via a live `dotnet run` + curl pass: anonymous access to a protected route redirects to `/Account/Login?ReturnUrl=...`, register auto-signs-in, logout clears the session, login re-authenticates, and a wrong password is rejected with a clear error.

### `CW-STORY-01.4` Establish engineering baseline
**Release:** MVP  
**Priority:** P1  
**Effort:** S
**User story:** As a team, we want build and test standards so changes remain stable over time.

**Acceptance criteria**
- Build can run consistently
- Basic test execution is available
- Logging defaults are configured

**Tasks**
- [x] Add analyzer and formatting settings
- [x] Configure structured logging baseline
- [x] Add smoke test coverage
- [x] Add CI-oriented build and test commands to docs

**Status:** Done — `Directory.Build.props` centralizes `Nullable`, `ImplicitUsings`, and .NET analyzers (`EnableNETAnalyzers`, `AnalysisLevel=latest-recommended`, `EnforceCodeStyleInBuild`) across all 7 projects; per-project `.csproj` files no longer duplicate those settings. Root `.editorconfig` added (generated via `dotnet new editorconfig`), with a scoped override so `CA1707` doesn't fight the xUnit `Method_Scenario_Expected` naming convention under `tests/`. Logging defaults tuned in `appsettings.json` (quiet `Microsoft.EntityFrameworkCore`/`Identity` categories) and `appsettings.Development.json` (`Microsoft.EntityFrameworkCore.Database.Command: Information` to see generated SQL locally). Smoke coverage added where there's real behavior to smoke — a `CartWiseWebApplicationFactory`-based integration suite (`tests/CartWise.Web.Tests/SmokeTests.cs`) boots the full app (DI, EF Core/SQLite, Identity, routing) against an isolated temp SQLite file and asserts: home page loads, an anonymous request to an `[Authorize]` route redirects to `/Account/Login`, and both auth pages load. `CartWise.Domain.Tests`/`CartWise.Application.Tests` still carry only the template placeholder test — there's no real domain/application logic yet to smoke-test; meaningful coverage there starts with `CW-STORY-02.1`. CI-oriented commands documented in `README.md` (`dotnet restore/build/test` against `CartWise.sln`; analyzers enforced at build time, not a separate lint step).

---

## `CW-EPIC-02` Household and Access Control

**Goal:** enable household creation, membership, and household-scoped authorization.

### `CW-STORY-02.1` Model households and membership
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a developer, I want household entities persisted so household features have a secure foundation.

**Acceptance criteria**
- Household and membership entities exist
- Roles are defined
- EF configurations and migration are created

**Tasks**
- [x] Create `Household` entity
- [x] Create `HouseholdMember` entity
- [x] Add role enum
- [x] Add EF configurations
- [x] Create migration
- [x] Add domain tests

**Status:** Done — `Household` and `HouseholdMember` live in `CartWise.Domain.Entities`, `HouseholdRole` (`Owner`/`Member`) in `CartWise.Domain.Enums`. `Household` is the aggregate root: its constructor auto-creates an `Owner` membership for the creator, `AddMember` rejects duplicate users, and `HouseholdMember`'s constructor is `internal` so members can only be created through the aggregate — Domain stays free of any ASP.NET Core/Identity reference (`CreatedByUserId`/`UserId` are plain strings, not `ApplicationUser` navigations). `HouseholdConfiguration`/`HouseholdMemberConfiguration` in `CartWise.Infrastructure/Data/Configurations` establish the real FK to `AspNetUsers` (`Restrict` on `Household.CreatedByUserId` to protect household history from user-deletion cascades; `Cascade` on `HouseholdMember` rows), a composite PK on `(HouseholdId, UserId)`, and an index on `HouseholdMember.UserId` for "which households does this user belong to" lookups. `AddHousehold` migration created and verified applying cleanly against a scratch SQLite database (and implicitly on every Web smoke test run, since those auto-migrate on boot). 11 domain tests added covering the invariants above.

### `CW-STORY-02.2` Implement household service
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a developer, I want household operations centralized so controllers stay thin and rules stay testable.

**Acceptance criteria**
- Household creation is supported
- Current household can be resolved
- Membership checks are reusable

**Tasks**
- [x] Create `IHouseholdService`
- [x] Implement `HouseholdService`
- [x] Add create household use case
- [x] Add get current household use case
- [x] Add membership verification logic
- [x] Add application tests

**Status:** Done — `IHouseholdService`/`HouseholdService` live in `CartWise.Application.{Interfaces,Services}`, exposing `CreateHouseholdAsync` (returns `Result<Household>`), `GetCurrentHouseholdAsync`, and `IsMemberAsync` (the reusable membership check `CW-STORY-02.4` will build authorization on top of). One household per user is enforced at this layer — see `DEC-009`. Data access goes through a new `IApplicationDbContext` abstraction (`CartWise.Application.Interfaces`, exposing just the `DbSet<Household>`/`DbSet<HouseholdMember>` needed) rather than a generic repository, since AGENTS.md explicitly rules out "generic repository abstractions added only for pattern compliance" — `CartWiseDbContext` implements it directly and is registered against the interface in `AddInfrastructure`. Time comes from the built-in `TimeProvider` (registered as `TimeProvider.System`) rather than a bespoke clock abstraction, keeping `CreateHouseholdAsync` deterministically testable without extra packages. `AddApplication()` (new `CartWise.Application` DI extension) is called from `Program.cs` alongside `AddInfrastructure`. 6 application tests added using EF Core's InMemory provider against the real `CartWiseDbContext` (via a new `CartWise.Infrastructure` reference in `CartWise.Application.Tests`, scoped to tests only — production `CartWise.Application` still has zero reference to `CartWise.Infrastructure`).

### `CW-STORY-02.3` Build household UI
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a user, I want to create and view my household so I can begin using CartWise.

**Acceptance criteria**
- Household create page is available
- Household overview page is available
- Validation errors are shown clearly

**Tasks**
- [x] Create `HouseholdController`
- [x] Create household view models
- [x] Build `Views/Household/Create.cshtml`
- [x] Build `Views/Household/Index.cshtml`
- [x] Add validation messaging
- [x] Add redirect flow after creation

**Status:** Done — `HouseholdController` (`[Authorize]`, attribute-routed to `/household` and `/household/create` per AGENTS.md §11) with `HouseholdIndexViewModel`/`HouseholdMemberViewModel`/`CreateHouseholdViewModel` in `CartWise.Web.ViewModels.Household`. `Index` resolves member display names via `UserManager<ApplicationUser>` in the controller (not the Application layer, to keep `CartWise.Application` free of Identity references) and redirects to `Create` when the signed-in user has no household yet; `Create` redirects back to `Index` if a household already exists (enforcing `DEC-009`'s one-per-user rule at the UI layer too, on top of the service-layer guard). Added a "Household" nav link, visible only when authenticated. Only Create + Index were built — member invite/remove (`/household/members/*`) are explicitly out of scope per AGENTS.md, which marks invite `[future/simple v1 if desired]`, and `HouseholdService` has no add/remove-member use case yet to back them. Verified live end-to-end via `dotnet run` + curl: anonymous-to-registered-to-household-created-to-overview-shown flow, member display name and `Owner` role render correctly, revisiting `/household/create` after a household exists redirects to `/household`, and an empty-name submission redisplays the form with visible validation errors (200, not a redirect).

### `CW-STORY-02.4` Enforce household authorization
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a user, I want household data isolated so other users cannot access it.

**Acceptance criteria**
- Household membership is enforced on household-scoped resources
- Unauthorized requests are blocked
- Resource isolation is covered by tests

**Tasks**
- [x] Add household authorization policy or helper
- [x] Enforce membership checks in services
- [x] Add integration tests for access control
- [x] Add tests for unauthorized and cross-household access

**Status:** Done — added a reusable resource-based `"HouseholdMember"` authorization policy: `HouseholdMemberRequirement` + `HouseholdMemberAuthorizationHandler` (`CartWise.Web.Authorization`), backed by the `IHouseholdService.IsMemberAsync` check already built in `CW-STORY-02.2`, registered via `AddAuthorizationBuilder()` in `Program.cs`. Note on current scope: `HouseholdController`'s existing actions (`Index`/`Create`) never accept a client-supplied household id — they always resolve "my household" from the authenticated user's own membership — so there's no IDOR surface to protect on them today; the new policy exists so the first household-scoped resource that *does* take an id (starting with `CW-EPIC-03`'s shopping list) can apply `[Authorize(Policy = "HouseholdMember")]`/resource-based `AuthorizeAsync` immediately instead of hand-rolling the check. Verified with: a unit-style test exercising the policy through the real DI container (`HouseholdAuthorizationTests` — two real users/households, confirms the policy succeeds for the owning member and fails for a user from a different household), and Web integration tests (`HouseholdAccessControlTests`) proving an anonymous request to `/household` redirects to login, and that two independently-registered users each only ever see their own household's name on `/household` — never each other's.

---

## `CW-EPIC-03` Smart Shopping List

**Goal:** allow household members to maintain a shared grocery list, including unresolved free-text items.

### `CW-STORY-03.1` Model shopping list and grocery concepts
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a developer, I want shopping list entities defined so shared list workflows can be persisted.

**Acceptance criteria**
- Grocery concept, category, shopping list, and shopping list item entities exist
- Item statuses are defined
- Optimistic concurrency is supported for list items

**Tasks**
- [x] Create `GroceryConcept`
- [x] Create `ProductCategory`
- [x] Create `ShoppingList`
- [x] Create `ShoppingListItem`
- [x] Add list status enums
- [x] Add row version concurrency token
- [x] Add EF configurations
- [x] Create migration

**Status:** Done — `ProductCategory`, `GroceryConcept`, `ShoppingList` (aggregate root with an `Items` backing-field navigation, mirroring `Household`/`HouseholdMember`), and `ShoppingListItem` added in `CartWise.Domain.Entities`; `ShoppingListStatus` (`Active`/`Completed`/`Archived`) and `ShoppingListItemStatus` (`Needed`/`Purchased`/`Skipped`/`Unavailable`/`Substituted`) in `CartWise.Domain.Enums`. Concurrency: `ShoppingListItem.RowVersion` is a `Guid` configured with `.IsConcurrencyToken()` rather than `.IsRowVersion()` — SQLite has no native auto-generated rowversion column, and `IsConcurrencyToken()` works identically on SQLite and a future PostgreSQL move (EF includes the original value in the `WHERE` clause either way); regenerating it on mutation is deferred to `CW-STORY-03.4`, which is where mutation methods land. Scope note: unlike `Household.AddMember`, no `ShoppingList.AddItem(...)` aggregate method exists yet — `ShoppingListItem`'s constructor is public for now since 03.1 is pure modeling and `CW-STORY-03.3` owns the add-item use case; that story should consider tightening `ShoppingListItem`'s constructor to `internal` behind an aggregate method, matching the `Household` pattern. `ShoppingListItem.PreferredProductId` from the AGENTS.md §5.4 spec was intentionally omitted — `Product` doesn't exist until the post-MVP `CW-EPIC-04`, and MVP explicitly doesn't require product resolution; it will be added via a future migration once `Product` exists. `AddShoppingList` migration created and verified applying cleanly against a scratch SQLite database.

### `CW-STORY-03.2` Implement active shopping list service
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a user, I want my household to have an active grocery list so shared planning is easy.

**Acceptance criteria**
- Active list can be retrieved
- Default active list can be created
- Household rule for default active list is enforced

**Tasks**
- [x] Create `IShoppingListService`
- [x] Implement get active list
- [x] Implement create list
- [x] Enforce one default active list rule
- [x] Add application tests

**Status:** Done — `IShoppingListService`/`ShoppingListService` in `CartWise.Application.{Interfaces,Services}`, following the exact pattern established by `HouseholdService` in `CW-STORY-02.2`: `GetActiveListAsync` (read, includes `Items`) and `CreateListAsync` (returns `Result<ShoppingList>`, rejects a second active list per the AGENTS.md §5.4 v1 rule with a clear error message). `IApplicationDbContext` extended with `ShoppingLists`/`ShoppingListItems` DbSets. Registered in `AddApplication()`. 6 application tests added (EF InMemory, same harness as `HouseholdServiceTests`), including a cross-household isolation check mirroring the pattern from `CW-STORY-02.4`. Also fixed a `CA1861` warning surfaced by the `AddShoppingList` migration's composite-column indexes by scoping a suppression to `Data/Migrations/**` in `.editorconfig` — migrations are generated code and shouldn't be hand-edited to satisfy analyzers.

### `CW-STORY-03.3` Add grocery items quickly
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a household member, I want to add free-text grocery items quickly so I do not lose shopping intent.

**Acceptance criteria**
- Free-text item entry is supported
- Quantity and unit are optional
- Item appears on the active list
- Product resolution is not required to add an item

**Tasks**
- [x] Implement add item use case
- [x] Support `DisplayName`, quantity, and unit
- [x] Add validation rules
- [x] Add item partial view
- [x] Add controller POST action
- [x] Add tests

**Status:** Done — `ShoppingList.AddItem(...)` aggregate method added (mirrors `Household.AddMember`; `ShoppingListItem`'s constructor is now `internal`), computing `SortOrder` and rejecting adds to a non-active list. `IShoppingListService.AddItemAsync` added. `ShoppingListController` (`GET /list`, `POST /list/items`) auto-provisions a household's first list (named "Groceries") on first visit — no separate "create list" UI step, matching AGENTS.md's "one active list is enough for MVP" and the First End-to-End Slice in `CLAUDE.md`. `Views/ShoppingList/Index.cshtml` + `_ListItem.cshtml` built (functional, Bootstrap-styled; the dedicated mobile-first/touch-friendly pass is `CW-STORY-03.5`). Scope note carried from `CW-STORY-03.1`'s status resolved: the constructor tightening happened here as anticipated.

Two real bugs found and fixed via testing, not just the earlier apostrophe-encoding test bug:
1. **EF Core state-tracking bug**: adding a new `ShoppingListItem` purely by mutating the already-tracked parent `ShoppingList`'s backing-field collection (`list.AddItem(...)`) left the new item's state ambiguous to EF Core's change tracker — since `ShoppingListItemId` is a client-generated `Guid` (not store-generated), EF couldn't reliably infer `Added` vs `Modified`, and attempted an `UPDATE` on a row that didn't exist yet, throwing `DbUpdateConcurrencyException`. Fixed by explicitly calling `_db.ShoppingListItems.Add(item)` in `ShoppingListService.AddItemAsync` after `list.AddItem(...)`.
2. **Model-binding prefix mismatch**: `Views/ShoppingList/Index.cshtml`'s tag helpers generate field names like `NewItem.DisplayName` (based on `ShoppingListViewModel.NewItem`'s property path), but `ShoppingListController.AddItem` bound a bare, unprefixed `AddShoppingListItemViewModel model` parameter — so nothing bound, `DisplayName` silently came through empty, and (surprisingly) no validation error surfaced either. Fixed with `[Bind(Prefix = "NewItem")]` on the action parameter.

Both were caught by genuine integration tests (`ShoppingListAccessControlTests`, real HTTP + real SQLite) written for this story, not by the weaker EF-InMemory application tests alone — worth remembering for future controller/view-model pairs that use a nested form-section pattern like this one. Extracted `WebTestHelpers` (register/create-household/extract-antiforgery-token) out of `HouseholdAccessControlTests` into a shared file so `ShoppingListAccessControlTests` didn't duplicate it. Verified live end-to-end via `dotnet run` + curl, matching `CLAUDE.md`'s First End-to-End Slice exactly: register → create household → `/list` auto-creates "Groceries" → add "Milk" (2 gal) → reload shows it with `Needed` status; empty-name submission correctly redisplays the form with a visible validation error (200, not a redirect).

### `CW-STORY-03.4` Update shopping list items
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a household member, I want to toggle, skip, and delete items so the list stays accurate.

**Acceptance criteria**
- Items can be marked purchased
- Items can be skipped and unskipped
- Items can be deleted
- Concurrency conflicts are handled safely

**Tasks**
- [ ] Implement toggle purchased
- [ ] Implement skip and unskip
- [ ] Implement delete item
- [ ] Handle concurrency exceptions
- [ ] Add progressive enhancement with jQuery
- [ ] Preserve server-post fallback
- [ ] Add tests

### `CW-STORY-03.5` Deliver a mobile-first list UI
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a shopper, I want a touch-friendly grocery list so it works well on my phone.

**Acceptance criteria**
- List page is readable on mobile
- Quick add is prominent
- Tap targets are appropriate for shopping use

**Tasks**
- [ ] Build `Views/ShoppingList/Index.cshtml`
- [ ] Build `Views/ShoppingList/_ListItem.cshtml`
- [ ] Add quick-add UI
- [ ] Add touch-friendly styles
- [ ] Add `wwwroot/js/pages/shopping-list.js`

---

## `CW-EPIC-04` Product Catalog and Barcode Resolution

**Goal:** support product identity, household product preferences, and local-first barcode resolution with provider fallback.

### `CW-STORY-04.1` Model product catalog entities
**Release:** Post-MVP  
**Priority:** P1  
**Effort:** M  
**Decision Gate:** Confirm whether a product catalog is still needed before barcode work begins.
**User story:** As a developer, I want product and identifier entities modeled so products can be resolved independently of UPC.

**Acceptance criteria**
- Brand, product, identifier, and household preference entities exist
- Identifier uniqueness rules are defined
- EF configurations and migration are created

**Tasks**
- [ ] Create `Brand`
- [ ] Create `Product`
- [ ] Create `ProductIdentifier`
- [ ] Create `HouseholdProductPreference`
- [ ] Add source type enums
- [ ] Add indexes and normalized fields
- [ ] Add EF configurations
- [ ] Create migration

### `CW-STORY-04.2` Search and view products
**Release:** Post-MVP  
**Priority:** P1  
**Effort:** M
**User story:** As a user, I want to search and view products so I can understand and choose preferred items.

**Acceptance criteria**
- Local product search works
- Product details page displays key product information
- Household price insights can be shown later on the details page

**Tasks**
- [ ] Create `IProductService`
- [ ] Implement local catalog search
- [ ] Create `ProductController`
- [ ] Build `Views/Product/Search.cshtml`
- [ ] Build `Views/Product/Details.cshtml`
- [ ] Add product view models
- [ ] Add tests

### `CW-STORY-04.3` Implement barcode lookup flow
**Release:** Deferred  
**Priority:** P2  
**Effort:** M  
**Decision Gate:** Barcode scanning is out of MVP scope.
**User story:** As a shopper, I want a barcode lookup flow so known products can be resolved quickly.

**Acceptance criteria**
- Barcode input is normalized
- Local identifier lookup runs first
- Provider fallback runs when local lookup misses
- Accepted results persist locally

**Tasks**
- [ ] Define `IProductDataProvider`
- [ ] Implement barcode normalization
- [ ] Implement local-first lookup flow
- [ ] Persist imported products and identifiers
- [ ] Add unit and application tests

### `CW-STORY-04.4` Integrate Open Food Facts
**Release:** Deferred  
**Priority:** P2  
**Effort:** M  
**Decision Gate:** External providers are out of MVP scope.
**User story:** As a developer, I want a provider fallback for unknown products so barcode resolution remains useful.

**Acceptance criteria**
- Open Food Facts provider can query by barcode
- Provider models remain isolated to infrastructure
- Provider errors are logged safely

**Tasks**
- [ ] Implement `OpenFoodFactsProductDataProvider`
- [ ] Map provider responses to internal DTOs
- [ ] Add configuration and secrets support
- [ ] Add error handling and logging
- [ ] Add tests with mocked provider behavior

### `CW-STORY-04.5` Build scan page
**Release:** Deferred  
**Priority:** P2  
**Effort:** M  
**Decision Gate:** Camera scan flow is out of MVP scope.
**User story:** As a shopper, I want a camera-based scan page so I can identify products with my phone.

**Acceptance criteria**
- Scan page requests camera permission
- Barcode result can resolve product details
- User can add scanned product to the list or record a price

**Tasks**
- [ ] Create `ScanController`
- [ ] Build `Views/Scan/Index.cshtml`
- [ ] Add camera permission flow
- [ ] Add barcode JS integration
- [ ] Add add-to-list and record-price actions

---

## `CW-EPIC-05` Stores, Purchases, and Price Intelligence

**Goal:** record where purchases happened and build a trustworthy append-only price history.

### `CW-STORY-05.1` Model retailers and store locations
**Release:** MVP  
**Priority:** P1  
**Effort:** S  
**Decision Gate:** Decide whether store tracking in MVP is minimal or omitted from first purchase flow.
**User story:** As a developer, I want retailer and store entities so purchases can be tied to real locations.

**Acceptance criteria**
- Retailer and store entities exist
- Location fields support practical v1 store data
- EF configurations and migration are created

**Tasks**
- [ ] Create `Retailer`
- [ ] Create `StoreLocation`
- [ ] Add EF configurations
- [ ] Create migration
- [ ] Add tests for basic persistence rules

### `CW-STORY-05.2` Model purchases and purchase items
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a developer, I want purchase history modeled so shopping outcomes can be stored.

**Acceptance criteria**
- Purchase and purchase item entities exist
- Purchase source types are defined
- EF configurations and migration are created

**Tasks**
- [ ] Create `Purchase`
- [ ] Create `PurchaseItem`
- [ ] Add source type enum
- [ ] Add EF configurations
- [ ] Create migration
- [ ] Add tests

### `CW-STORY-05.3` Model append-only price observations
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a developer, I want append-only price observations so historical price facts are preserved.

**Acceptance criteria**
- Price observation entity exists
- Useful indexes are defined for history queries
- Older observations are not overwritten by newer ones

**Tasks**
- [ ] Create `PriceObservation`
- [ ] Add indexes for product, household, and store history
- [ ] Add EF configuration
- [ ] Create migration
- [ ] Add tests for append-only behavior

### `CW-STORY-05.4` Record household purchases
**Release:** MVP  
**Priority:** P0  
**Effort:** M  
**Decision Gate:** Decide whether MVP purchase entry must link directly to shopping list items or can begin as a manual flow.
**User story:** As a shopper, I want to record purchases so CartWise can remember what I bought and what I paid.

**Acceptance criteria**
- Purchase flow can start and complete
- Purchase items can be added
- Shopping list items can be linked when relevant

**Tasks**
- [ ] Create `IPurchaseService`
- [ ] Implement start purchase flow
- [ ] Implement add purchase item flow
- [ ] Implement complete purchase flow
- [ ] Link purchase items to shopping list items where applicable
- [ ] Add tests

### `CW-STORY-05.5` Derive price intelligence
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a user, I want price history and price insights so I can make better grocery decisions.

**Acceptance criteria**
- Purchase items can create price observations
- Latest, average, median, lowest, and unit price calculations are supported when data allows
- Freshness and source can be displayed

**Tasks**
- [ ] Create `IPriceService`
- [ ] Derive observations from purchase items
- [ ] Implement latest price calculation
- [ ] Implement average and median calculations
- [ ] Implement lowest recent price calculation
- [ ] Implement unit price calculation
- [ ] Add tests for price statistics

### `CW-STORY-05.6` Build price views
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a user, I want a price history screen so I can review what my household typically pays.

**Acceptance criteria**
- Prices index page exists
- Product-specific price history page exists
- Product details can show historical price insight

**Tasks**
- [ ] Create `PriceController`
- [ ] Build `Views/Price/Index.cshtml`
- [ ] Build `Views/Price/Product.cshtml`
- [ ] Add price history to product details view
- [ ] Add filtering and sorting UI
- [ ] Add freshness and source display

---

## `CW-EPIC-06` Shopping Mode

**Goal:** provide a simple, touch-friendly in-store workflow for marking progress and recording purchases.

### `CW-STORY-06.1` Implement shopping mode service
**Release:** Post-MVP  
**Priority:** P1  
**Effort:** S  
**Decision Gate:** Decide whether a dedicated shopping mode is needed beyond the list page.
**User story:** As a developer, I want shopping-mode orchestration so in-store behavior is consistent and testable.

**Acceptance criteria**
- Shopping mode view can be prepared from list data
- Items can be grouped for easier shopping
- Remaining and purchased counts are available

**Tasks**
- [ ] Create `IShoppingModeService`
- [ ] Implement shopping mode preparation
- [ ] Group by category-based sections
- [ ] Calculate remaining and purchased counts
- [ ] Add tests

### `CW-STORY-06.2` Build shopping mode UI
**Release:** Post-MVP  
**Priority:** P1  
**Effort:** M
**User story:** As a shopper, I want a low-noise mobile shopping view so I can use CartWise during a real trip.

**Acceptance criteria**
- Shopping mode works well on a phone
- Groups and counts are clear
- Key actions are easy to reach

**Tasks**
- [ ] Create `ShoppingController`
- [ ] Build `Views/Shopping/Start.cshtml`
- [ ] Build `Views/Shopping/_ShoppingItem.cshtml`
- [ ] Add touch-friendly controls
- [ ] Add `wwwroot/js/pages/shopping-mode.js`

### `CW-STORY-06.3` Support in-store item actions
**Release:** Post-MVP  
**Priority:** P1  
**Effort:** M
**User story:** As a shopper, I want to mark items purchased, skipped, unavailable, or substituted so the trip stays accurate.

**Acceptance criteria**
- Item transitions are supported
- Anti-forgery is enforced on state changes
- Actions update the shopping workflow correctly

**Tasks**
- [ ] Add purchase action
- [ ] Add skip action
- [ ] Add unavailable action
- [ ] Add substitute action
- [ ] Add anti-forgery for AJAX and form posts
- [ ] Add tests

### `CW-STORY-06.4` Record prices while shopping
**Release:** Post-MVP  
**Priority:** P1  
**Effort:** S
**User story:** As a shopper, I want to optionally enter prices in shopping mode so price history stays current.

**Acceptance criteria**
- Optional price entry is available during purchase actions
- Money input is validated
- Captured values feed purchase and price history logic

**Tasks**
- [ ] Add optional per-item price input
- [ ] Validate price and quantity inputs
- [ ] Persist captured values through purchase service
- [ ] Add tests

---

## `CW-EPIC-07` Running-Low Suggestions

**Goal:** use deterministic purchase history to suggest what a household may need soon.

### `CW-STORY-07.1` Build replenishment engine
**Release:** MVP  
**Priority:** P0  
**Effort:** M
**User story:** As a developer, I want deterministic running-low logic so suggestions are explainable and testable.

**Acceptance criteria**
- Last N purchase dates can be evaluated
- Median interval is used when sufficient history exists
- Minimum-history and tolerance rules are enforced

**Tasks**
- [ ] Create `IReplenishmentService`
- [ ] Gather recent purchase dates
- [ ] Calculate purchase intervals
- [ ] Implement median interval logic
- [ ] Add minimum-history thresholds
- [ ] Add tolerance window logic
- [ ] Return score and reason data
- [ ] Add tests

### `CW-STORY-07.2` Show suggestions on home and list views
**Release:** MVP  
**Priority:** P0  
**Effort:** S  
**Decision Gate:** Confirm the minimum launch dashboard: active list count, recent prices, and running-low suggestions.
**User story:** As a user, I want running-low suggestions surfaced in context so I can act on them quickly.

**Acceptance criteria**
- Home page can display suggestions
- Shopping list page can display suggestions
- Suggestions include reason or confidence context

**Tasks**
- [ ] Extend `HomeViewModel`
- [ ] Add suggestions to home controller flow
- [ ] Add running-low partial views
- [ ] Render suggestions on list page
- [ ] Add tests

### `CW-STORY-07.3` Add suggestion-to-list workflow
**Release:** MVP  
**Priority:** P1  
**Effort:** S
**User story:** As a user, I want to add a running-low suggestion to my list so I can convert insight into action quickly.

**Acceptance criteria**
- Suggestion can be added to the active list
- Existing list workflows are reused
- Duplicate accidental adds are reasonably controlled

**Tasks**
- [ ] Add controller action to add suggestion
- [ ] Reuse shopping list add-item workflow
- [ ] Add duplicate-prevention rules if needed
- [ ] Add tests

---

## `CW-EPIC-08` UX Polish, Security, and Release Readiness

**Goal:** harden the v1 experience for real-world use and release confidence.

### `CW-STORY-08.1` Improve mobile usability and accessibility
**Release:** MVP  
**Priority:** P1  
**Effort:** S
**User story:** As a user, I want the app to be easy to use on my phone and accessible across core workflows.

**Acceptance criteria**
- Key pages are responsive
- Labels, focus states, and contrast meet baseline usability needs
- Error handling is understandable

**Tasks**
- [ ] Review responsive layouts
- [ ] Improve form labels and focus states
- [ ] Improve contrast and tap sizes
- [ ] Validate keyboard navigation
- [ ] Validate error summaries

### `CW-STORY-08.2` Harden security coverage
**Release:** MVP  
**Priority:** P0  
**Effort:** S
**User story:** As a team, we want security-sensitive paths reviewed so household data remains protected.

**Acceptance criteria**
- Anti-forgery coverage is verified
- Authorization coverage is verified
- Secrets handling is consistent with app rules

**Tasks**
- [ ] Review anti-forgery coverage
- [ ] Review authorization enforcement
- [ ] Review validation across state-changing flows
- [ ] Review secrets and configuration handling
- [ ] Add integration tests for protected routes

### `CW-STORY-08.3` Add diagnostics and logging
**Release:** MVP  
**Priority:** P1  
**Effort:** S
**User story:** As a team, we want actionable diagnostics so failures can be understood and fixed quickly.

**Acceptance criteria**
- External provider failures are logged
- Barcode misses can be diagnosed
- Authorization failures are logged safely

**Tasks**
- [ ] Add structured logging for provider failures
- [ ] Log unresolved barcode outcomes
- [ ] Log authorization failures safely
- [ ] Add basic health-check strategy if included

### `CW-STORY-08.4` Seed realistic development data
**Release:** Post-MVP  
**Priority:** P2  
**Effort:** M
**User story:** As a developer, I want realistic sample data so workflows can be tested quickly during development.

**Acceptance criteria**
- Development-only seed data exists
- Seed data covers household, products, stores, and price history
- Production is not polluted with fake data

**Tasks**
- [ ] Seed one household
- [ ] Seed grocery concepts and categories
- [ ] Seed brands and products
- [ ] Seed product identifiers
- [ ] Seed store locations
- [ ] Seed purchase and price history
- [ ] Restrict seeding to development only

### `CW-STORY-08.5` Harden HTML assertions in Web integration tests
**Release:** MVP  
**Priority:** P2  
**Effort:** XS  
**User story:** As a developer, I want Web integration test assertions to be resilient to HTML encoding so tests don't produce false negatives on ordinary characters in test data.

**Acceptance criteria**
- Web integration test assertions against rendered HTML compare against HTML-decoded content, not raw encoded output
- Test data is not artificially restricted to avoid HTML-special characters (apostrophes, ampersands, quotes)

**Tasks**
- [x] Add an HTML-decoding assertion helper to `CartWise.Web.Tests`
- [x] Update `HouseholdAccessControlTests` to use realistic household names (including an apostrophe) via the new helper
- [x] Note the pattern so future integration tests use it by default

**Status:** Done — added `HtmlAssert.Contains`/`DoesNotContain` (`tests/CartWise.Web.Tests/HtmlAssert.cs`), which runs `WebUtility.HtmlDecode` before comparing. `HouseholdAccessControlTests` now uses `"Alice's Household"` / `"Bob's Household"` (real apostrophes) through the new helper and passes. Future Web integration tests asserting against rendered HTML should use `HtmlAssert` rather than raw `Assert.Contains`/`DoesNotContain`.

**Origin:** Discovered during `CW-STORY-02.4` verification — `EachUser_OnlySeesTheirOwnHousehold` failed against `"Alice's Household"` because Razor correctly HTML-encodes `'` as `&#x27;` in `@Model.Name`, but the test asserted a literal apostrophe. The immediate fix (switching to apostrophe-free test data) papered over the real issue: any test data with `&`, `'`, `"`, `<`, or `>` would hit the same false negative. This story is the real fix.

---

## Suggested 30-Day Delivery Sequence

### Week 1 - Foundation
- `CW-EPIC-01` Platform Foundation
- Start `CW-EPIC-02` Household and Access Control

### Week 2 - Shared List
- Finish `CW-EPIC-02` Household and Access Control
- Start and finish core stories in `CW-EPIC-03` Smart Shopping List

### Week 3 - Purchases and Price History
- Deliver `CW-EPIC-05` MVP stories

### Week 4 - Running Low and Hardening
- Deliver `CW-EPIC-07` MVP stories
- Deliver selected `CW-EPIC-08` hardening stories
- Run local MVP release checklist

### After MVP
- Resume `CW-EPIC-04` Product Catalog and Barcode Resolution
- Resume `CW-EPIC-06` Shopping Mode
- Complete remaining polish work in `CW-EPIC-08`

## Story Template

Use this format for new backlog items:

```md
### `CW-STORY-XX.X` Story Title
**User story:** As a `user type`, I want `capability` so that `benefit`.

**Acceptance criteria**
- Criterion 1
- Criterion 2

**Tasks**
- [ ] Task 1
- [ ] Task 2
```

## Notes

- Keep v1 scope tightly aligned to the grocery-memory goals
- Avoid introducing speculative architecture or out-of-scope features
- Reprioritize stories only when dependencies or product value clearly require it
- Preserve the implementation order unless a small change is needed to unblock progress
