Repository navigation
Conversation
Complete specification for replacing the tutorialProgress.isComplete dashboard gate with a five-state operational map (empty/new, partly stocked, cooking, returning, error), a Home-owned read model, an optional restartable learning panel, and a Linux home-flow-check package. Spec only — implementation not started; see PR Handoff. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The operational Home flow core from planning/operational-home-flow-spec.md: - HomeFlowModels.swift: the pure five-state map (emptyNew, partlyStocked, cooking, returning, error) over repository outputs, with explicit primary actions. Tutorial progress is not an input anywhere; unknown expiry is nil, never zero. - HomeFlowReadModel.swift: one repository-backed read pass producing both the state-map inputs and the existing dashboard snapshot, plus a pure HomeLoadGeneration staleness guard. Combine-free so it compiles on Linux. - HomeFlowViewModel.swift: the thin observable shell (app target only; ObservableObject is not available on Linux) wiring GRDB live observers to the guarded load. - Tools/home-flow-check: a Linux Swift package mirroring historical-nutrition-check: refresh.sh copies the real production sources in, tests drive them through real GRDB databases. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
The body gate in HomeDashboardView no longer forks on tutorial progress: it resolves HomeFlowStateMap.state(from:cookContext:) and switches on the result. Tutorial cards remain only for a genuinely empty-new kitchen whose tour is incomplete; a stocked or returning cook now sees their kitchen even if they skipped the tour, and the cooking state reads solely the Live Assistant coordinator's surfaced matched-recipe context. A snapshot-level projection (state(from:cookContext:)) documents the shipped view's seam: same precedence rules as the canonical inputs path, evaluated over the snapshot the sections already consume. It never reports .error — the nil-snapshot branch keeps owning that. ContentView.swift and the Home init surface are byte-identical (proved in the PR body: the view diff's only hunks start at line 102, inside body). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Implementation record for planning/operational-home-flow-spec.md: the five states with precedence, the binding rules (unknown expiry nil never zero, tutorial progress not an input, cooking reads only the surfaced Assistant context), the canonical and fork-projection derivation paths, the read model and generation guard, and how the shipped view wires them. Written so the redesigned Home UI can sit on this core unchanged. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Keeps the PR diffable against the moved base; no rebase, no history rewrite. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
SamGu-NRX
left a comment
There was a problem hiding this comment.
Verdict: FIX
Reviewed head 427c5c7 against fl-integration-f1, Part 5.
Blockers:
- apps/ios/Feature/Home/HomeFlowModels.swift:249: the shipped projection treats snapshot.ingredientCount as pantry stock. That value comes from IngredientRepository.count(), which counts catalog rows, not inventory. An onboarded user with a populated catalog, zero lots and zero cooks resolves partlyStocked instead of emptyNew. Feed actual active inventory into the shipped gate and add the catalog-present/empty-pantry regression. The existing projection fixtures assign inventory-like counts and miss this case.
- apps/ios/Feature/Home/HomeDashboardView.swift:114: emptyNew still gates the operational dashboard on tutorialProgress.isComplete. The saved spec requires optional practice rather than a dashboard gate. HomeFlowReadModel and HomeFlowViewModel are also never instantiated by production Home, so their tested read path and generation guard do not protect the shipped view. Connect the real read result while preserving the init surface, or explicitly narrow the deliverable and obtain acceptance of that change.
Criteria: five-state core partly met, read/view-model pair partly met, Home-only fork and unchanged ContentView/init met, Linux behavioral coverage partly met, explicit UNVERIFIED-NATIVE handoff met. Fully met 2/5. The tests omit depleted/returning repository transitions and relaunch, and the no-tutorial-field test asserts a fixture tautology.
CI run 38144868415 is green at this exact SHA. No local build, Xcode or simulator run. No workflow or protected-file edits found. The existing seven-day shelf-life fallback is inherited behavior, not reviewed food-safety evidence.
Home treated IngredientRepository.count() — the recipe-catalog size — as "stocked kitchen" evidence, so a user with a stocked pantry and an empty catalog (or vice versa) resolved the wrong operational state, and the shipped dashboard still gated the kitchen on tutorial progress through its own catalog-count view model. - HomeFlowReadModel derives stock evidence from active inventory lots (distinct ingredients with remaining grams > 0) and gates recommendations on that evidence; the catalog count no longer feeds the pass. - HomeDashboardSnapshot.stockedIngredientCount carries the same quantity evidence; the projection reads it, so the canonical and shipped paths agree. - HomeDashboardView now holds HomeFlowViewModel over the flow read model and switches its fork on the canonical state map; the empty-new kitchen renders the operational dashboard with the tutorial as optional practice below it. The init signature is byte-identical. - The catalog-count HomeDashboardViewModel is deleted; the unreachable tutorial landing went with it (its practice pieces live in optionalPracticeSection). - home-flow-check grows regression tests: populated catalog with empty pantry (both derivation paths), distinct-ingredient quantity evidence, partly-stocked to empty-new depletion, depleted stock with cooked meals staying returning, relaunch across a reopened database, and a structural no-tutorial-input contract over the real stored surface. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
SamGu-NRX
left a comment
There was a problem hiding this comment.
Verdict: FIX
Reviewed 735883e4748a9f0ea9fbe2c13c9626075faa29bb against fl-integration-f1 Part 5 and the saved operational Home spec.
The previous findings are resolved. HomeFlowReadModel.swift:135-148 derives stock from active inventory with positive quantity. HomeDashboardView.swift:75-85,112-128,330-336 now instantiates the shared read/view-model pair and renders the dashboard for empty-new users regardless of tutorial completion. The public init parameters and ContentView are unchanged. New repository tests cover catalog-without-stock, depletion, returning after depletion and reopening the same database.
Blockers:
None found in the reviewed delta.
Lesser findings:
- The Proof and Handoff still name
427c5c7, claim 38 tests, and say the StateObject assignment was untouched. The current delta changes that assignment, removes HomeDashboardViewModel and adds tests. Record current-head home-flow-check execution and replace the obsolete byte-identity claim with the actual public-signature guarantee. Native app CI does not run this separate package. HomeDashboardView.swift:125-128,330-335offers practice only in empty-new state while the tour is incomplete. Once stock arrives, or the user skips/completes practice, that entry disappears. The saved spec calls for optional restartable practice in every state. Mark this presentation requirement unfinished or provide an entry without resetting kitchen data. The new dashboard gate itself works.HomeFlowViewModel.swift:74,83silently discards observer errors. Surface a refresh failure or recovery route so a dead observation does not leave the last dashboard indefinitely presented as current.- The inherited seven-day shelf-life default is not reviewed food-safety evidence. The read layer preserves genuinely unknown expiry as nil; its existing fridge-default test does not establish that seven days is safe for each ingredient.
Task criteria: five-state core met; repository-backed read/view-model pair met; Home-only fork with unchanged ContentView/public init met; Linux behavioral coverage partly met because current-head execution is unreported; explicit UNVERIFIED-NATIVE Handoff met. Fully met 4/5. No workflow or protected-file changes appear in this delta.
CI run 38190050413 passed native build-and-test at this exact head. Rendering, Dynamic Type and VoiceOver remain unverified. No local tests, Xcode or simulator were run.
Implements the non-visual core of the FL4 operational Home flow (
planning/operational-home-flow-spec.md). This PR was spec-only until332ac36; the implementation now follows in three milestones plus a clean base merge. Full implementation record:planning/operational-home-flow-implementation.md.Summary
Home gated its dashboard behind
tutorialProgress.isComplete(HomeDashboardView.bodyforked on@AppStorage("tutorialProgressStorage")quest progress), so a user who stocked a real fridge but skipped the tour saw quest cards instead of a kitchen. The spec replaces that fork — inside Home-owned files only — with a pure five-state map (emptyNew,partlyStocked,cooking,returning,error) built from public repository outputs, plus a read model/view model pair and a Linuxhome-flow-checkpackage cloning the provenhistorical-nutrition-checkpattern. That replacement is now implemented and Linux-verified (38/38 tests).Behavior change, in one line: tutorial quest cards appear only for a genuinely empty-new kitchen with an incomplete tour; a stocked or returning cook now sees their kitchen even if they skipped the tour; a live cook sees the cooking context.
Decisions made
HomeDashboardView.swift;ContentView.swiftand the 13-member Home init surface stay byte-identical.TutorialProgress/TutorialQuesttypes and the storage key keep their current write behavior (quest completion, skip, reset), so Progress-tab gating is untouched.cookingstate reads only the Live Assistant coordinator's context surfaced as a value — no persisted active-cook record is invented (none exists on this branch).package.jsonis an unrelated Electron manifest); the check record covers package tests plus repo audit scripts.Implementation (this branch, in order)
332ac36—HomeFlowModels.swift: the pure five-state map with explicit primary actions; tutorial progress is not an input anywhere in the flow core (compile-time surface); unknown expiry is nil, never zero.HomeFlowReadModel.swift: one repository-backed read pass producing both the state-map inputs and the existing dashboard snapshot from the same reads, plus the pureHomeLoadGenerationstaleness guard; Combine-free so it compiles on Linux.HomeFlowViewModel.swift: the thin observable shell (app target only —ObservableObjectdoes not exist on Linux) wiring the same GRDB cooking-history and inventory live observers Home already used to the guarded load.apps/ios/Tools/home-flow-check/: package mirroringhistorical-nutrition-check—Scripts/refresh.shcopies 24 real production sources into the ignoredSources/HomeFlowCheck/Real/; tests drive them through real migrated GRDB databases.42ff652— the gate inHomeDashboardView.bodynow resolvesHomeFlowStateMap.state(from: snapshot, cookContext:)and switches on the result. A snapshot-level projection (state(from:cookContext:)) documents the shipped view's seam: same precedence rules, evaluated over the snapshot the sections already consume; it never reports.error— the nil-snapshot branch keeps owning that. The cooking context is built solely fromLiveAssistantCoordinator.matchedRecipeContext(the surfaced value), mapped toHomeCookContext(recipeName:); coordinator internals are never read.d2d1d57—planning/operational-home-flow-implementation.md: state map, read model, generation guard, wiring, and verification documented so the redesigned Home UI can sit on this core unchanged.427c5c7— merge of basec9555fc(obv/fl-next-historical-nutrition-r1moved by one commit:CutoverParityTests.swift; clean merge, no rebase, no history rewrite). Suite re-proven green on the merged tree.Proof
All commands run at head
427c5c7ac764f4d29e198b25cfe8d3a61f51f11aon Linux x86_64, Swift 6.4.1. Full suite —
bash apps/ios/Tools/home-flow-check/Scripts/refresh.sh && swift test --package-path apps/ios/Tools/home-flow-checkCoverage: 23 state-map tests (every state, every precedence overlap, every primary action, the cook-context arriving/leaving transition seam, the no-tutorial-input contract), 6 fork-projection tests (including agreement with the canonical
HomeFlowInputspath and never-error for non-nil snapshots), 9 read-model tests (real-DB cold start, stocked kitchen with a makeable recipe, unknown-expiry-nil, fridge shelf-life derivation (+7-day writer default, pinned as behavior), use-soon surfacing, pre-onboarding stock hiding, generation guard, read→gate seam).2. Byte-identity proof —
git diff 372f6f3..HEADBoth hunks start at line 102 — inside
body. The 13-member init surface (lines 48–88) and the_viewModel = StateObject(...)line are untouched.3. Repo audits
(The audits' earlier "NSCameraUsageDescription missing" error was a false positive from
rgnot being installed in the sandbox, not a finding; ripgrep was installed locally at/home/user/.local/bin/rgand both audits then passed. The offline audit's network-grep lists only pre-existing intended integrations — Gemini clients, notification sync, GRDB package URLs — plus the same GRDB package URL declaration the toleratedhistorical-nutrition-checkcarries.)4. Commit attribution — every commit on this branch is authored by Sam Gu with the required trailer:
5. Native CI compile evidence — iOS CI (
ios-ci.yml, macOS build-and-test onmacos-26, PR-triggered) completed on this exact head: run 38144868415 —status: completed,conclusion: success,head_sha: 427c5c7ac764f4d29e198b25cfe8d3a61f51f11a(fetched from the Actions API this session). This green covers macOS compilation of the SwiftUI changes and the macOS test targets; it does not cover visual rendering, Dynamic Type, VoiceOver, or XCUITest checks — those stay UNVERIFIED-NATIVE below.Handoff
Done
HomeDashboardView.swiftonly;ContentView.swiftand the Home init surface proven byte-identical (Proof §2).planning/operational-home-flow-implementation.md).c9555fcin via427c5c7); no rebase, no force-push, no workflow edits, base left asobv/fl-next-historical-nutrition-r1.Not done (UNVERIFIED-NATIVE — no macOS host in this workspace)
Next steps
planning/operational-home-flow-implementation.md(10 minutes), then the Proof above; the redesigned-Home UI work builds directly onHomeFlowStateMap.state(inputs:cookContext:)+HomeFlowReadResultper that doc.refresh.sh && swift test, and re-verify byte-identity ofContentView.swiftand the init region.Risks / notes
ContentView.swift; this branch does not.HomeFlowViewModelis app-target-only by platform necessity (ObservableObject); its logic lives in the Linux-testedHomeLoadGeneration+HomeFlowStateMap.Proof - home stock evidence fix (2026-10-11)
Scope of this fix. Home surfaces (canonical state map, read model, dashboard projection, and the shipped gate) now derive "stocked kitchen" evidence from active inventory quantities — distinct ingredients with remaining grams > 0 — never from the recipe-catalog count. Production
HomeDashboardViewis routed throughHomeFlowViewModel+HomeFlowReadModel(the read model built for this PR). Until the canonical food-safety and FLIngredientFacts packages land, no composition tables were added and unknown composition continues to fail closed. Append-only section; the Handoff above predates this fix and names head427c5c7— the current head is735883e4748a9f0ea9fbe2c13c9626075faa29bb, which supersedes it.1. Linux suite —
apps/ios/Tools/home-flow-check(real repositories, real migrated GRDB schema, no network):New regressions added for the reviewer findings (44 total, up from 38):
stockedIngredientCountcounts distinct ingredients with remaining grams > 0 (three lots across two ingredients + a zero-gram lot → 2, not the catalog count and not the lot count).provenance 'logged_at_capture').HomeFlowInputsexposes no tutorial-progress field — asserted over the real stored surface withMirror, not against a literal.2. Byte-identity proofs (required by the fix constraints):
The Home
initparameter surface is byte-identical to the previous head;ContentView.swiftuntouched; nothing under.github/.3. Audits (repo scripts, both exit 0):
4. Commit attribution — the fix commit is authored by Sam Gu with the required trailer:
5. Native CI compile evidence — iOS CI on this exact head: run 38190050413 — both jobs
completed / success(build-and-teston macOS, anddead-code-advisory; fetched from the Actions API this session). This covers macOS compilation of the rewiredHomeDashboardView(including the deletedHomeDashboardViewModel) and the macOS test targets; it does not cover visual rendering, Dynamic Type, VoiceOver, or XCUITest — those remain UNVERIFIED-NATIVE.Pending the canonical packages (not started, per instructions):
overhaul/food-safety-canonand the FLIngredientFacts package land,HomeFlowReadModelis the single integration point — itsingredientsdependency is already injected and its load pass is documented for that swap inplanning/operational-home-flow-implementation.md.🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact