Repository navigation
Kitchen AI Phase 1a: tonight recipe recommendations with deterministic kitchen facts - #32
Draft
1395490241lhj-code wants to merge 4 commits into
Draft
1395490241lhj-code wants to merge 4 commits into
1395490241lhj-code wants to merge 4 commits into
Conversation
Home meal questions now preload inventory and an app-ranked library shortlist, so the model picks and explains instead of guessing or re-emitting full recipes. Recipe cards show deterministic kitchen facts. - Meal intent needs a food cue and a choice cue; English cues match whole words only. Home only; Planner unchanged. - Shortlist (<= 8, recipeID/title/missingCount/usesExpiring) excludes tonight's planned recipes and is withheld with inventory. - KitchenAIStockReading is shared by shortlist and card: in stock / missing from availableInventory, use-up hint from recipeCreationExpiringItems; ranking alone uses recipeCreationInventory (D-024). RecipeStockMatch gains Index and summary(for:), rule unchanged. - Presence-only copy: "所需食材已在库 · 用量未核对" replaces "可直接做". - VoiceOver announcements, offline send guard, adjustment chips that only prefill the composer, and the "今晚吃什么" Home starter. - Server: drop plannedServings from add-to-tonight and replace-meal schemas the client rejects; describe add-to-tonight truthfully. No change to the six content blocks, five proposals, coordinator, interpreter, orchestrator, persistence, confirmation or undo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Home preloads inventory and the recipe shortlist only for questions that clearly ask what to cook or eat. Ambiguous wording now preloads nothing; the model can still read the kitchen through its tools. - Chinese intent comes from joined cooking phrases, not single characters or words that merely co-occur: 吃什么 asked as the question, 想吃 with a choice, taste or dish, a dish phrase where 菜 ends it, a meal of the day joined to a cooking verb or choice, or a cooking noun with a choice word. - Negatives such as 饭店推荐, 饭卡, 吃药, 菜馆, 菜单, 菜市场, 早餐店, 午餐会议, 晚饭后吃什么药 and 我不想吃药 now read only tonight's plan; tests assert the real reads. Planner, exclusions and the context budget are unchanged. - The all-present stock row reads "主要食材在库 · 用量与调料未核对", with a no-break space so the dot never opens a line at accessibility sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 20, 2026
Brings PR #31 (iOS review fixes) and PR #34 (Kitchen AI failure diagnostics) under the Phase 1a work. - test/ai-conversation-stream.test.mjs: both sides appended tests. Keep main's file whole (all 43 PR #34 tests, S1/S2 diagnostics included) and append this branch's servings test, rewritten for the protocol below. - src/server/services/ai-conversation.js: keep plannedServings on propose_add_recipe_to_tonight and propose_replace_planned_meal. This branch had removed it because the old client refused it; PR #31's interpreter now accepts 1-12 and refuses anything else, so the schema offers the optional field with that range in its description. The low-risk wording for add-to-tonight is kept: the client executes it after validation without a confirmation step. - The other shared iOS files merged without conflict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s end to end - ConversationContextAssembler.isMealQuestion now reads a question clause by clause. A clause about food someone else cooks (食堂, 餐厅, 外卖, 老板, 附近 ...) or about a medicine (药, not 药膳) does not read inventory or the shortlist, unless the same clause names the family's own kitchen (家里, 在家, 冰箱 ...). Other clauses still count, so 「不想点外卖,在家 做什么菜」 stays a cooking question. New pattern (f) covers 家里有鸡蛋能 做什么 / 冰箱里的东西能做点什么. The inventory exclusion, the Home tonightPlan read and the context budget are unchanged. - Tests check the domain reads and the final request context: eating-out and medicine questions read only tonightPlan and carry no inventory facts or missing counts; home-cooking questions read inventory, tonightPlan and the shortlist; excluding inventory still withholds both. - ConversationOrchestratorTests: four end-to-end cases from a provider tool call through the interpreter, coordinator and store: tonight for four, no or null count keeps the existing rule, 0/13/2.5/"4" is refused with nothing written, and a replacement for four previews "4 份" and writes it on confirm. The helper keeps the orchestrator alive for the whole stream, since its turn task holds it weakly. - AIConversationWorkspaceUITests.bringIntoView: when the target sits above the visible band (at AX sizes 继续调整 pushes the last card up), scroll back in small steps; otherwise scroll down as before. Same direction check as PR #33's 329cc50; nothing else from that PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Kitchen AI Phase 1a, "今晚吃什么". Home dinner questions now give the model the real kitchen up front. The model only chooses and explains. Recipe cards show inventory facts the app computes itself.
This PR adds no new content-block type, no new proposal type and no dependency. Card-level 加入今晚 (Phase 1b) is not included. It needs a separate owner decision.
Commits:
d06ce32feat(ios): improve Kitchen AI tonight recipe recommendationsbcca20afix(ios): tighten Kitchen AI meal intent and stock copyb089149Merge main into claude/hopeful-volhard-9272e9 (brings in PR iOS: fix bugs and logic issues found in the UX and functional reviews #31 and PR fix(server): diagnose Kitchen AI provider failures #34; see "Integration with main")5a6b15afix(ios): read the kitchen only for cooking at home, and test servings end to endWhat changes for the member (iOS, Home → Kitchen AI)
Home starters are now 今晚吃什么 / 用快过期的食材做饭 / 今晚想吃清淡一点 / 帮我补一道菜. 今晚吃什么 replaces 看看现在能做什么.
Meal questions preload the kitchen. A Home meal question loads inventory plus an app-ranked shortlist from the recipe library. The model can then reference library recipes by
recipeIDinstead of guessing or writing out full recipes.Recipe cards show deterministic kitchen rows:
These rows are presence-only. Quantities and seasonings are never checked, and the copy says so. The model's reason is shown only as the reason.
Adjacent recipe cards in one reply group into a vertical run. Each card is one accessibility element with a full spoken summary.
继续调整 chips appear after a completed turn that has recipe cards: 换清淡一点的菜 / 换快手一点的菜 / 只用家里有的食材做菜 / 换一批菜. A tap only prefills the composer; it never sends.
VoiceOver announcements cover turn completion, failure, awaiting confirmation, offline, and action/undo outcomes. They only say what the screen already shows.
Offline guard. A send is blocked only when the network path is explicitly
.unavailable;.unknownstill sends. When blocked:Server (
src/server/services/ai-conversation.js):propose_add_recipe_to_tonightandpropose_replace_planned_mealkeep an optionalplannedServings(integer). Its description states the 1–12 range the client accepts. (bcca20ahad removed it, because the old client refused it. PR iOS: fix bugs and logic issues found in the UX and functional reviews #31's interpreter now accepts it, sob089149restores it.).lowrisk with no confirmation step.Final implementation facts
Meal intent (
ConversationContextAssembler.isMealQuestion). It applies to the Home entry only. If none of the rules below fires, nothing is preloaded.5a6b15a.5a6b15a). The question is split at punctuation, and each clause is judged on its own:Shortlist (
recipeCandidatesContext):recipeID,title,missingCountandusesExpiring.recipeCreationInventory(D-024), then whether it uses something expiring, then library order.Stock reading (
KitchenAIStockReading, shared by the shortlist and the card):availableInventory. This is the same answer as the library's 还缺 N 样 and Recipe Detail.recipeCreationExpiringItems.RecipeStockMatchonly gainsIndexandsummary(for:). The presence rule is unchanged.Live context budget. The total is still 3000 characters. It is now filled smallest source first: a short source's unused share passes to longer ones, and no source gets less than an equal split.
Unchanged: the six
AIContentBlockcases, the fiveAIActionProposalcases,ConversationActionCoordinator,ConversationResponseInterpreter,ConversationOrchestrator, persistence, confirmation and undo. The controller's confirm and undo paths only gained announcements.Integration with main (
b089149)main@45b108dwas merged in with a normal merge commit. It brings PR #31 (iOS review fixes) and PR #34 (Kitchen AI failure diagnostics).test/ai-conversation-stream.test.mjs. Both sides had appended tests at the end of the file.main's file is kept whole: all 43 PR fix(server): diagnose Kitchen AI provider failures #34 tests, including the S1/S2 diagnostics, with 0 lines removed.main:mainare byte-identical tomain, including all 8de14ffdfiles.mainis one this branch had itself removed.plannedServings1–12 on the two single-meal proposals and refuse anything else. The server schema now offers that field again (see Server above).ConversationOrchestratorTestsdrive a provider tool call through the real interpreter, coordinator and store:null, keeps the existing rule;Privacy fix (
5a6b15a)The false positives listed in the earlier review are fixed by the clause rule above.
ConversationContextAssemblerTestshad 37 expected assertion failures in the two new cases:tonightPlanand carry no inventory facts or missing counts: 早餐吃什么药, 今天食堂吃什么, 老板推荐什么菜, 推荐几家做菜好吃的餐厅, 附近有什么好吃的饭店, 今晚想出去吃饭, 今晚想出去吃什么, 外卖吃什么好, 午饭吃什么药好, 餐厅推荐什么菜.tonightPlanand the shortlist: 今晚吃什么, 用快过期的食材做饭, 推荐一道家常菜, 家里有鸡蛋能做什么, 今晚四个人吃什么, 冰箱里的东西能做点什么, 不想点外卖,在家做什么菜, 不点外卖了家里有鸡蛋能做什么.tonightPlanread and the context budget are unchanged.Test-only change from PR #33.
AIConversationWorkspaceUITests.bringIntoViewnow scrolls back when the target sits above the visible band. At AX sizes the 继续调整 chips push the last card up. This is the same direction check as PR #33's329cc50; nothing else from that PR is included. With it,testStructuredBlocks{Light,Dark}AccessibilityXXXLpass. Before it, both failed on the merged tree with the card scrolled above the band, and the Light case had already failed the same way onbcca20a.Validation
Final tree
5a6b15a, localAll local iOS runs used
xcodebuildthroughdev-sim-sloton the reserved simulator (iPhone 18 Pro, iOS 27.2) with parallel testing off. Xcode MCP was held by another worktree, so it was not used.KitchenManagerTestsde14ffdRecipePersistence / SwiftDataConsistencynpm test(full Node suite)The full unit run was taken on the tree before the UI-test helper edit, and that edit is the only file changed since.
server.jsand everyde14ffdfile are identical tomain. No SwiftData model or schema changes.CI on
5a6b15aConversationOrchestratorTests70/70,ConversationContextAssemblerTests56/56, D-050ConsumptionPersistenceTests20/20 andKitchenStoreTests49/49,de14ffdRecipePersistenceTests13/13 andSwiftDataConsistencyTests6/6, andStatusStapleConsumptionTests7/7AIConversationWorkspaceUITests+AIConversationAcceptanceUITests(iOS 26.5)testStructuredBlocks{Light,Dark}AccessibilityXXXLpassed (58.8 s / 66.4 s). Unit target 2660/0 againScreenshots from 39788704292. 20 Tonight and 20 AIBlocks captures, covering Light/Dark × Normal/AXXXL.
bringIntoView, the 家常豆腐 draft card is fully in view with 查看菜谱 and 继续调整 below it.Since CI ran,
mainmoved to207ca09(PR #36, native motion). It overlaps this PR only inContentView.swift, in different DEBUG-fixture hunks, and merges cleanly. No CI has run on that combination.Not run yet: real VoiceOver listening; a physical device.
Screenshot review guide (20 PNGs)
The images are local review evidence. They are not attached here. Location:
~/Library/Application Support/Claude/side-session-notes/local_b1036e8d-…/tonight-phase1a-screenshots/.They are named
Tonight-{Light|Dark}-{Normal|AXXXL}-{Starters|CardReady|CardShortfall|CardDraft|Adjustments}.png. At normal size, CardReady/CardShortfall and CardDraft/Adjustments are the same frame.Check:
The
iOS testsworkflow runs on pushes toclaude/**. It runs the wholeKitchenManagerTestsunit target plus its default UI selection, and itsios-screenshotsartifact has the screenshot tour. The default UI selection does not includeAIConversationWorkspaceUITests, so CI does not recapture the Tonight images. To have CI recapture them, dispatch the workflow withui_tests: KitchenManagerUITests/AIConversationWorkspaceUITests.Remaining open privacy risk (owner decision, not changed here)
The
isMealQuestionfalse positives listed in the earlier review are fixed in5a6b15a; see "Privacy fix".One pre-existing contract remains, unchanged on
mainand on this branch:inventoryKeywords(库存/食材/过期/临期/冰箱/…) read inventory on any entry whenever the word appears, regardless of setting. For example, 「餐厅的食材新鲜吗」 reads inventory, though not the shortlist.Known false negative: 「想吃宵夜」 does not preload. The model can still read the kitchen through its tools.
Effect on PR #33 (stacked on this branch)
PR #33 (
claude/kitchen-ai-reliability@329cc50) is still based onbcca20aand was not touched. When it is integrated, merge this branch into it; do not rebase. Expect:ConversationContextAssembler.swiftand its tests:isMealFollowUpand new instructions will conflict textually with the clause-basedisMealQuestionand with PR iOS: fix bugs and logic issues found in the UX and functional reviews #31's date line.server.jsandtest/ai-conversation-stream.test.mjs: PR fix: improve Kitchen AI reliability and conversation scrolling #33 carries an older diagnostics copy (20a0de2). Take PR fix(server): diagnose Kitchen AI provider failures #34's version frommaininstead.AIConversationWorkspaceUITests.swift: thebringIntoViewhunk is now identical on both branches.AIConversationView.swiftandUITestAIConversationTransport.swiftchanged on all three lines of work.Vault reconciliation
Current Status.head_commitis45b108d(PR iOS: fix bugs and logic issues found in the UX and functional reviews #31 merged). It stays there until this PR merges; then reconcile through the merge commit onmain.KitchenAIStockReading.inventoryKeywordsrisk;Non-goals
No Phase 1b card actions. No OpenUI or other new dependency. No migration, sync, flag, credential or hosted-config change. No change to Inventory or Planner surfaces.
🤖 Generated with Claude Code