Skip to content

Checklist mode for when the supermarket API is down - #447

Merged
adri merged 7 commits into
masterfrom
adri/checklist-mode
Oct 12, 2026
Merged

adri merged 7 commits into
masterfrom
adri/checklist-mode

Conversation

@adri

@adri adri commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

The supermarket API is down (Akamai 503 HTML on every endpoint), which broke planning and the shopping list. Picape now has a second place the list can live, switched by a setting row:

  • ah — unchanged: the plan syncs into the supermarket basket.
  • checklist — the same computed list (planned recipes merged with manual quantities) served straight from the database. No supermarket call happens on the read path; Order.current builds the line via LineFromDb and overlays the ticks.

Six commits:

  • Answer a clear error when the supermarket is down — a non-JSON body no longer raises a bare Poison.SyntaxError; Supermarket.Error names the status and the first 200 chars of the body, so MCP tool calls and GraphQL report what came back instead of crashing. The fake can serve the same page via POST /__down.
  • Checklist mode in the domain — setting + checklist_item tables, Picape.Settings, Picape.Checklist (tick/untick, refused outside checklist mode), Order.set_mode/1. Switching back to ah pushes the accumulated list into the basket and reports pushed_to_supermarket; a still-down supermarket does not block the switch, the next sync retries. Closing an order clears the ticks.
  • GraphQL — Order.mode, OrderItem.checked, checkItem(ingredientId:, checked:), setMode(mode:), both publishing order_changed.
  • MCP — new set_mode and check_item tools; get_shopping_list now returns mode and per-item checked. The tool descriptions spell out when to use them, so the grocery agent can fall over to checklist mode on its own.
  • App — checklist rows lead with a check circle (Reminders-style), ticked rows dim and strike through, and a "Naar checklist" / "Naar supermarkt" line under the delivery day switches modes. The close-out reads "Boodschappen gedaan" in checklist mode.
  • Screen tests — reset-plan also restores ah mode and clears ticks; a new spec drives the whole loop over MCP with the fake down, ticks in the UI, and reads the state back over MCP.

Checklist mode: ticked Chicken row, unticked Rijst, Naar supermarkt switch

Impact

impact:
  not-measurable: Picape has no metrics pipeline for sync behaviour; correctness is covered by deterministic tests against the supermarket fake.

Expected runtime effect: while api.ah.nl keeps answering 503, switching to checklist mode (one MCP set_mode call) restores the shopping list, planning and ticking without any supermarket call; switching back pushes the list into the basket.

Test plan

  • mix check — 133 passed (15 new: checklist domain, outage handling, MCP surface)
  • cd frontend && npm run check — ESLint, Prettier, graphql:check, 13/13 Jest
  • bin/e2e — 89 passed / 10 skipped; new checklist spec runs on all three projects
  • Production: run the migration, switch to checklist mode while the outage lasts

cc @adri

Generated with Devin

adri and others added 7 commits October 12, 2026 20:15
The API answers an Akamai 503 HTML page during its outage, and
process_response_body raised a raw Poison.SyntaxError over it. Decode
failure now keeps the body and process_response raises
Supermarket.Error naming the status and the start of the body, so MCP
tools and resolvers can report what actually came back. The fake can
answer the same page via POST /__down and /__up.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
A `setting` row holds the shopping mode: "ah" keeps syncing the plan
into the supermarket basket, "checklist" serves the same computed list
from the database instead, so planning, ordering and reading the list
keep working while the supermarket API is unreachable. `checklist_item`
stores which ingredients are ticked off; Order overlays them onto the
line and closing the order clears them. Switching back to "ah" pushes
the accumulated list into the basket and reports whether that worked;
a still-down supermarket does not block the switch.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Order gains a `mode` field, OrderItem a `checked` field, and the
mutations setMode and checkItem drive them. A failed push back to the
supermarket answers as a mutation error that still names the switch.
Both mutations publish order_changed so open clients catch up.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The grocery agent switches modes and ticks items over the same tools:
set_mode ah|checklist reports pushed_to_supermarket, check_item ticks
an ingredient on or off, and get_shopping_list reports the mode and
each item's checked state. Both writes publish order_changed; the
payload is a placeholder since the subscription re-resolves the order.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
In checklist mode each shopping-list row leads with a check circle
that ticks the item off through checkItem, and ticked rows dim and
strike through. A "Naar checklist" / "Naar supermarkt" line under the
delivery day switches modes through setMode; in checklist mode the
close-out button reads "Boodschappen gedaan".

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
reset-plan now also restores ah mode and drops the ticks, both rows a
test would otherwise leave behind. The new spec switches to checklist
mode over MCP, takes the supermarket fake down, ticks an item in the
app and reads the same state back over MCP. The shop screenshots carry
the extra mode line in the heading.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The tool table names set_mode and check_item, and the notes explain
what get_shopping_list's mode field means and what switching back to
the supermarket does with the accumulated list.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@adri
adri marked this pull request as ready for review October 12, 2026 18:28
@adri
adri merged commit 8f7fae6 into master Oct 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant