Conversation
`aisle.conf` decides how the shopping list is grouped, but the web UI only printed its path, and an ingredient no aisle named fell silently under "other". A new /aisles page, reached from the shopping list and from Preferences, shows the aisles in file order and lets a shopper: - add, rename, reorder and remove aisles; - add, rename, move and remove ingredients and their `a | b` synonyms; - see the ingredients the recipes use that no aisle names (the `cook doctor aisle` check) and file each under an aisle; - edit the file as text, saved only once the aisle parser reads it; - start a `config/aisle.conf` where there is none. The shopping list's "other" items get an "Assign aisle" picker too. Edits go through `server::aisle_file`, which changes only the lines concerned, so comments and the blank lines that group a long aisle stay put; `cooklang::aisle::write` would have dropped both. Each change carries the revision of the file it was made against and is refused with 409 if the file moved on. Ingredient names are kept unique ignoring case, as the shopping list matches them. The server now also picks up an aisle file created after it started. The routes live under /api/aisles at `Capability::EditLists`, like the pantry; every write is recorded in the activity log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`AppState::aisle_file` sat in the impl block that only exists with `sync`, so `--no-default-features --features server,lsp` (the Docker image's build) did not compile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LeMyst
marked this pull request as ready for review
October 5, 2026 23:22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #630.
A new /aisles page, linked from the shopping list header ("Manage aisles") and from Preferences next to the aisle file's path, manages
aisle.conffrom the browser.What it does
cook doctor aislecheck, viacookcli_core::doctor::aisle_coverage), each with links to the recipes using it and an aisle picker.cooklang::aisle::parseaccepts it; if not, the error names the line.config/aisle.confin the recipe directory. The server now uses an aisle file that appears after startup (AppState::aisle_file()), so no restart is needed.Design notes
src/server/aisle_file.rsreads lines the waycooklang::aisle::parsedoes and changes only the lines an edit is about. Comments, and the blank linesseed/config/aisle.confuses to group a long aisle, survive.cooklang::aisle::writewould drop both. Comments directly above an aisle move and are removed with it; CRLF files stay CRLF. In the end-to-end test, adding, moving and removing an ingredient leaves seed's file byte-identical.revision, a hash of the file text. A change sent with an outdated revision gets409, and the page reloads the aisles and asks you to try again. On the Text tab your typing is kept, and saving a second time replaces the other change. Changes are also serialised with an in-process lock.ingredients_infolowercases), and so are aisle names. A name with|or//is refused, since it would turn into a synonym or a comment.API
All under
/api/aislesand documented in/api-docsanddocs/api.md. Writes needCapability::EditLists(shopper and up), like the pantry, and are written to the activity log.GET /api/aislesPOST /api/aislesconfig/aisle.conf(201, or409if one exists)POST /api/aisles/changes{revision?, action, …}:add_aisle,rename_aisle,remove_aisle,move_aisle,add_ingredient,update_ingredient,remove_ingredientGET/PUT /api/aisles/rawGET /api/aisles/uncategorizedUnder
--recipes-only, guests cannot reach/aislesbecause of the existing allowlist.Tests
aisle_file.rs: 16 unit tests covering preserved lines, CRLF, moving blocks with their comments, case-insensitive duplicates and invalid names.tests/aisles_api_test.rs: 7 tests that bootcook server(throughwith_isolated_config). They cover: only the changed lines are touched,409on a stale revision,400/404cases, raw text validation, creating the file from nothing (the shopping list picks it up without a restart), and uncategorized.server_auth_test:/api/aisles/changesadded to the role matrix (reader refused, shopper and up allowed); middleware unit tests updated.tests/e2e/aisles.spec.ts: 6 Playwright tests. They restoreseed/config/aisle.confafter each test and stub only the shopping list's item list, so they don't race other specs over the shared list.locales_testpasses).cargo fmt,cargo clippy --all-targets(no warnings) andcargo test(1139 passed) are clean. The full Chromium suite locally: aisles, shopping-list and preferences specs all pass serially. With parallel workers, the only failures are the known shopping-list races between workers andstatic-file-search(needs acookbinary in the container), both seen onmainin earlier runs; the shopping-list specs pass when run serially with this change.🤖 Generated with Claude Code