The weekly shopping list generated from the planned dinners' ingredients, grouped by dinner, with unchecking as the only pantry mechanism, plus the blog-style Supply archive (requirements §9–§10; mocks 1l, 2b, 2f, 1m; D-020).
Tasks (all seven done, each independently reviewed at its exact head)
This branch also carries the cross-cutting runbook-to-CTXD correction (Maestro e81ed994, PR #137, approved at ccb6349), attached to this feature deliberately so it rides this promotion: it deletes deploy/RUNBOOK.md (1417 lines) and re-points every reference at CTXD project mealquest, file RUNBOOKS.MD, per the owner's binding directive that all runbook information lives in CTXD. The published CTXD runbook is version 6.
What the feature delivers
Deterministic shopping-list generation from the plan's normalized recipe ingredients (one group per plan day, one item per ingredient, needed defaulting true, unknown quantity null), with regeneration preserving the "have it" flag of unchanged items and skipping days with nothing to buy — no LLM in that path.
The API: GET /api/v1/shopping/{weekStart} (counts and groups), PATCH /shopping/items/{id} (new counts, 409 when archived), POST /shopping/{weekStart}/generate, and the Push-to-Calendar hook that generates the week's list and reports honestly (generated / archived / failed / plan_changed).
archive_title v1 (1–48 chars, no emoji, no surrounding quotes, repaired through the harness's one repair turn) run as a title_supply_run job, never blocking the list, retried on the next generation and scoped by the week, plan revision and the ordered dinner names.
Freeze and archive: Freeze is idempotent, only freezes ended weeks, titles an untitled list first through the same single title write and waits for an in-flight title job; GET /shopping/archive?by=week|month|year returns posts grouped in the household timezone with counts and current; archived lists serve read-only.
The Supply run screen (1l/2b/2f) with optimistic toggles that roll the item and count back on failure, a read-only archived rendering, a disabled "Send to Google Chat" until Feature 19, and a Plan-screen entry point with honest count states; an offline toggle queue (single serial scope, bounded deadline with backoff that needs no browser online event, at-least-once replay documented as safe because a toggle sets a fixed boolean, 409 on replay discarding the change); and the Supply archive screen (1m) with the period headings the API returns, the current-post highlight from the API's current, the null-title fallback and read-only frozen posts.
Integration checks at the feature head 680706df8389c56848f128f2114d72c69f350e6a (8 commits ahead of dev, 0 behind)
gofmt -l . — empty; go vet ./... — clean
go test -race -count=1 ./... — 27 packages ok, 0 failures
CGO_ENABLED=0 go build ./... — ok
make sqlc-check SQLC=$(go env GOPATH)/bin/sqlc — no drift
cd web && npm run typecheck PASS; npm test PASS 49 files / 872 tests; npm run build PASS
make test, make build (which sets -X main.version), docker build . — PASS
Migrations this feature adds: 0016_shopping_list_untitled.sql (title nullable, groups carry their plan day). The branch already contains all of dev.
Notes
Feature 16 was promoted to main (e9b7593) while this feature was in flight, so this promotion is the next one and carries the runbook removal.
Non-blocking findings were dispositioned with recorded rationale, including the review's recommendation that offline replay be at-least-once rather than exactly-once (the task criterion was amended accordingly) and a LOW set on the titling follow-up task a55adc28 in Feature 22.
## Feature 17 — Shopping List and Supply Archive
Integration pull request: `feature/3a88c1fd-6c9a-4e70-b6b1-a8880dd4ac82-shopping-list-and-supply-archive` -> `dev`.
The weekly shopping list generated from the planned dinners' ingredients, grouped by dinner, with unchecking as the only pantry mechanism, plus the blog-style Supply archive (requirements §9–§10; mocks 1l, 2b, 2f, 1m; D-020).
## Tasks (all seven done, each independently reviewed at its exact head)
| Task | Maestro | PR | Reviewed head |
| --- | --- | --- | --- |
| Shopping list generation from the plan | f098e8f5 | #132 | 1ca6693 |
| Shopping API and item toggles | d076480c | #133 | a31bbce |
| archive_title skill | fa4b94c9 | #134 | 1d19fb9 |
| Week rollover freeze and archive API | 7ac36526 | #135 | 292f917 |
| Supply run screen (1l, 2b, 2f) | 40cc2de5 | #136 | 1513571 |
| Offline toggle queue | a9689163 | #138 | 54b5996 |
| Supply archive screen (1m) and frozen post view | 3cf3a22a | #139 | 2488708 |
This branch also carries the cross-cutting **runbook-to-CTXD** correction (Maestro e81ed994, PR #137, approved at ccb6349), attached to this feature deliberately so it rides this promotion: it deletes `deploy/RUNBOOK.md` (1417 lines) and re-points every reference at CTXD project `mealquest`, file `RUNBOOKS.MD`, per the owner's binding directive that all runbook information lives in CTXD. The published CTXD runbook is version 6.
## What the feature delivers
- Deterministic shopping-list generation from the plan's normalized recipe ingredients (one group per plan day, one item per ingredient, `needed` defaulting true, unknown quantity null), with regeneration preserving the "have it" flag of unchanged items and skipping days with nothing to buy — no LLM in that path.
- The API: `GET /api/v1/shopping/{weekStart}` (counts and groups), `PATCH /shopping/items/{id}` (new counts, 409 when archived), `POST /shopping/{weekStart}/generate`, and the Push-to-Calendar hook that generates the week's list and reports honestly (`generated` / `archived` / `failed` / `plan_changed`).
- `archive_title` v1 (1–48 chars, no emoji, no surrounding quotes, repaired through the harness's one repair turn) run as a `title_supply_run` job, never blocking the list, retried on the next generation and scoped by the week, plan revision and the ordered dinner names.
- Freeze and archive: `Freeze` is idempotent, only freezes ended weeks, titles an untitled list first through the same single title write and waits for an in-flight title job; `GET /shopping/archive?by=week|month|year` returns posts grouped in the household timezone with counts and `current`; archived lists serve read-only.
- The Supply run screen (1l/2b/2f) with optimistic toggles that roll the item and count back on failure, a read-only archived rendering, a disabled "Send to Google Chat" until Feature 19, and a Plan-screen entry point with honest count states; an offline toggle queue (single serial scope, bounded deadline with backoff that needs no browser `online` event, at-least-once replay documented as safe because a toggle sets a fixed boolean, 409 on replay discarding the change); and the Supply archive screen (1m) with the period headings the API returns, the current-post highlight from the API's `current`, the null-title fallback and read-only frozen posts.
## Integration checks at the feature head `680706df8389c56848f128f2114d72c69f350e6a` (8 commits ahead of `dev`, 0 behind)
- `gofmt -l .` — empty; `go vet ./...` — clean
- `go test -race -count=1 ./...` — 27 packages ok, 0 failures
- `CGO_ENABLED=0 go build ./...` — ok
- `make sqlc-check SQLC=$(go env GOPATH)/bin/sqlc` — no drift
- `cd web && npm run typecheck` PASS; `npm test` PASS 49 files / 872 tests; `npm run build` PASS
- `make test`, `make build` (which sets `-X main.version`), `docker build .` — PASS
- Migrations this feature adds: `0016_shopping_list_untitled.sql` (title nullable, groups carry their plan day). The branch already contains all of `dev`.
## Notes
- Feature 16 was promoted to `main` (`e9b7593`) while this feature was in flight, so this promotion is the next one and carries the runbook removal.
- Non-blocking findings were dispositioned with recorded rationale, including the review's recommendation that offline replay be at-least-once rather than exactly-once (the task criterion was amended accordingly) and a LOW set on the titling follow-up task `a55adc28` in Feature 22.
Reviewed head: 680706df8389c56848f128f2114d72c69f350e6a. Base: dev at 7003ff92eccb15d7594266f904ce0d8ea9d7dcb4. The suite evidence supplied was all green and the reviewer did not rerun it.
F17-01 / HIGH / web/src/plan/api.ts lines 409–413 — the push client parses only the plan and discards the response's supply_run outcome. The server returns HTTP 200 with supply_run.status="failed" for a changed plan or a generation error (internal/server/plans.go:145), and PlanPage.tsx:360 surfaces failures only from push.error, so the household can be shown a successful Calendar sync while the Supply run failed to generate. This breaks the fail-fast / no-silent-fallback rule. Fix: parse and surface every supply_run status, offering Push again for plan_changed and list generation for supply_run_failed.
F17-02 / MEDIUM / web/src/plan/api.ts:413 — a successful push updates the plan cache but never invalidates the Supply run cache (web/src/api/queryClient.ts:14 gives a 30s freshness window), so a cached 404 or the previous list can stay visible right after the list is created or regenerated. Fix: invalidate that week's Supply run query on the push response.
F17-03 / MEDIUM / migrations/queries/shopping.sql:73 (ListShoppingGroupDinners) — archive reads join the current dinner name rather than the name as it was at freeze, so renaming a dinner later rewrites the displayed history of a frozen Supply run. Deferred with recorded rationale (see the follow-up task) because the fix is a schema/data decision and migration 0016 has been reviewed as safe for the live database; changing it would invalidate that review.
Migration 0016: reviewed as safe for the live Feature 16 production database — the shopping tables had no application write path, the rebuild is transactional, and the upgrade tests cover populated valid rows plus rollback for an undated group; applied migrations are unchanged. The disabled Google Chat action and the absence of a scheduler match the Feature 19/20 boundaries.
Remediation for F17-01 and F17-02 is being implemented on a task branch of this feature; the feature head will move and this pull request will be re-reviewed at the new head before merge.
## Feature-level review (fresh read-only session) — `CHANGES_REQUIRED`
Reviewed head: `680706df8389c56848f128f2114d72c69f350e6a`. Base: `dev` at `7003ff92eccb15d7594266f904ce0d8ea9d7dcb4`. The suite evidence supplied was all green and the reviewer did not rerun it.
**F17-01 / HIGH / `web/src/plan/api.ts` lines 409–413** — the push client parses only the plan and discards the response's `supply_run` outcome. The server returns HTTP 200 with `supply_run.status="failed"` for a changed plan or a generation error (`internal/server/plans.go:145`), and `PlanPage.tsx:360` surfaces failures only from `push.error`, so the household can be shown a successful Calendar sync while the Supply run failed to generate. This breaks the fail-fast / no-silent-fallback rule. Fix: parse and surface every `supply_run` status, offering Push again for `plan_changed` and list generation for `supply_run_failed`.
**F17-02 / MEDIUM / `web/src/plan/api.ts:413`** — a successful push updates the plan cache but never invalidates the Supply run cache (`web/src/api/queryClient.ts:14` gives a 30s freshness window), so a cached 404 or the previous list can stay visible right after the list is created or regenerated. Fix: invalidate that week's Supply run query on the push response.
**F17-03 / MEDIUM / `migrations/queries/shopping.sql:73` (`ListShoppingGroupDinners`)** — archive reads join the current dinner name rather than the name as it was at freeze, so renaming a dinner later rewrites the displayed history of a frozen Supply run. Deferred with recorded rationale (see the follow-up task) because the fix is a schema/data decision and migration `0016` has been reviewed as safe for the live database; changing it would invalidate that review.
**Migration `0016`:** reviewed as safe for the live Feature 16 production database — the shopping tables had no application write path, the rebuild is transactional, and the upgrade tests cover populated valid rows plus rollback for an undated group; applied migrations are unchanged. The disabled Google Chat action and the absence of a scheduler match the Feature 19/20 boundaries.
Remediation for F17-01 and F17-02 is being implemented on a task branch of this feature; the feature head will move and this pull request will be re-reviewed at the new head before merge.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Feature 17 — Shopping List and Supply Archive
Integration pull request:
feature/3a88c1fd-6c9a-4e70-b6b1-a8880dd4ac82-shopping-list-and-supply-archive->dev.The weekly shopping list generated from the planned dinners' ingredients, grouped by dinner, with unchecking as the only pantry mechanism, plus the blog-style Supply archive (requirements §9–§10; mocks 1l, 2b, 2f, 1m; D-020).
Tasks (all seven done, each independently reviewed at its exact head)
1ca6693a31bbce1d19fb9292f917151357154b59962488708This branch also carries the cross-cutting runbook-to-CTXD correction (Maestro e81ed994, PR #137, approved at
ccb6349), attached to this feature deliberately so it rides this promotion: it deletesdeploy/RUNBOOK.md(1417 lines) and re-points every reference at CTXD projectmealquest, fileRUNBOOKS.MD, per the owner's binding directive that all runbook information lives in CTXD. The published CTXD runbook is version 6.What the feature delivers
neededdefaulting true, unknown quantity null), with regeneration preserving the "have it" flag of unchanged items and skipping days with nothing to buy — no LLM in that path.GET /api/v1/shopping/{weekStart}(counts and groups),PATCH /shopping/items/{id}(new counts, 409 when archived),POST /shopping/{weekStart}/generate, and the Push-to-Calendar hook that generates the week's list and reports honestly (generated/archived/failed/plan_changed).archive_titlev1 (1–48 chars, no emoji, no surrounding quotes, repaired through the harness's one repair turn) run as atitle_supply_runjob, never blocking the list, retried on the next generation and scoped by the week, plan revision and the ordered dinner names.Freezeis idempotent, only freezes ended weeks, titles an untitled list first through the same single title write and waits for an in-flight title job;GET /shopping/archive?by=week|month|yearreturns posts grouped in the household timezone with counts andcurrent; archived lists serve read-only.onlineevent, at-least-once replay documented as safe because a toggle sets a fixed boolean, 409 on replay discarding the change); and the Supply archive screen (1m) with the period headings the API returns, the current-post highlight from the API'scurrent, the null-title fallback and read-only frozen posts.Integration checks at the feature head
680706df8389c56848f128f2114d72c69f350e6a(8 commits ahead ofdev, 0 behind)gofmt -l .— empty;go vet ./...— cleango test -race -count=1 ./...— 27 packages ok, 0 failuresCGO_ENABLED=0 go build ./...— okmake sqlc-check SQLC=$(go env GOPATH)/bin/sqlc— no driftcd web && npm run typecheckPASS;npm testPASS 49 files / 872 tests;npm run buildPASSmake test,make build(which sets-X main.version),docker build .— PASS0016_shopping_list_untitled.sql(title nullable, groups carry their plan day). The branch already contains all ofdev.Notes
main(e9b7593) while this feature was in flight, so this promotion is the next one and carries the runbook removal.a55adc28in Feature 22.Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
Feature-level review (fresh read-only session) —
CHANGES_REQUIREDReviewed head:
680706df8389c56848f128f2114d72c69f350e6a. Base:devat7003ff92eccb15d7594266f904ce0d8ea9d7dcb4. The suite evidence supplied was all green and the reviewer did not rerun it.F17-01 / HIGH /
web/src/plan/api.tslines 409–413 — the push client parses only the plan and discards the response'ssupply_runoutcome. The server returns HTTP 200 withsupply_run.status="failed"for a changed plan or a generation error (internal/server/plans.go:145), andPlanPage.tsx:360surfaces failures only frompush.error, so the household can be shown a successful Calendar sync while the Supply run failed to generate. This breaks the fail-fast / no-silent-fallback rule. Fix: parse and surface everysupply_runstatus, offering Push again forplan_changedand list generation forsupply_run_failed.F17-02 / MEDIUM /
web/src/plan/api.ts:413— a successful push updates the plan cache but never invalidates the Supply run cache (web/src/api/queryClient.ts:14gives a 30s freshness window), so a cached 404 or the previous list can stay visible right after the list is created or regenerated. Fix: invalidate that week's Supply run query on the push response.F17-03 / MEDIUM /
migrations/queries/shopping.sql:73(ListShoppingGroupDinners) — archive reads join the current dinner name rather than the name as it was at freeze, so renaming a dinner later rewrites the displayed history of a frozen Supply run. Deferred with recorded rationale (see the follow-up task) because the fix is a schema/data decision and migration0016has been reviewed as safe for the live database; changing it would invalidate that review.Migration
0016: reviewed as safe for the live Feature 16 production database — the shopping tables had no application write path, the rebuild is transactional, and the upgrade tests cover populated valid rows plus rollback for an undated group; applied migrations are unchanged. The disabled Google Chat action and the absence of a scheduler match the Feature 19/20 boundaries.Remediation for F17-01 and F17-02 is being implemented on a task branch of this feature; the feature head will move and this pull request will be re-reviewed at the new head before merge.
Independent Codex gpt-6-sol review: APPROVED for this exact commit.