Repository navigation
test(ios): wire the overhaul into the app and prove the fridge-to-meal-to-taste path - #182
Draft
obvious-autobuild[bot] wants to merge 20 commits into
Draft
obvious-autobuild[bot] wants to merge 20 commits into
obvious-autobuild[bot] wants to merge 20 commits into
Conversation
…endencies The packet PRs shipped the community client stack (#159/#166/#173/#179), the taste-profile repository (#165/#167/#176), and the LeChef plan sheet (#152/#178) with no app-side construction site. AppDependencies now builds them at launch: CommunityStore + CommunitySessionService (keychain token store) + the three community view models, and TasteProfileRepository over the same shipped-bundle feature index the residual ranker uses. The profile and plan-sheet view models are exposed through factories backed by the production adapters. The community backend URL resolves from COMMUNITY_BACKEND_BASE_URL (env or Info.plist), defaulting to the local dev server on port 8087. Co-authored-by: Sam Gu <sgu07966@gmail.com>
catalogMatches ranked every installed catalog row regardless of rights status. Filter to rights_status = 'original' so a rewritten recipe can never surface in Home's pool, matching the gate report's requirement that only originals are recommendable. Co-authored-by: Sam Gu <sgu07966@gmail.com>
…ndencies One end-to-end walk over a fresh on-disk database, shipped resources, and the real AppDependencies: scan-review pantry intake with a household allergen, Home's exact recommendation source under the off residual gate, a catalog cook on the guide's session construction with the structured timer and thermometer steps, a recorded timer_start proposal approved on the cooking session, the celebration-path meal log with the frozen (catalog_id, revision) nutrition snapshot and pantry consumption, the taste profile's cook evidence below the render gate, a weekly plan through the production adapter with allergen screening, and a signed-out share waiting in the community outbox. Co-authored-by: Sam Gu <sgu07966@gmail.com>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
PlanSheetViewModel lives in the FLFeatureLogic framework target; the new factory needs the module import to compile. Co-authored-by: Sam Gu <sgu07966@gmail.com>
LiveProposalFrames.proposal takes Data, not String; MealLogService.Outcome spells historyId with a lowercase d; and GRDB's sync read overload must be chosen from a sync context, so the async test reads through a synchronous helper shell. Co-authored-by: Sam Gu <sgu07966@gmail.com>
The previous round's mechanical edits were lost to a concurrent write; this commit re-applies them on top of the helper additions. Co-authored-by: Sam Gu <sgu07966@gmail.com>
GRDB's sync read is a plain throwing call, so a rethrows wrapper may not forward to it. Co-authored-by: Sam Gu <sgu07966@gmail.com>
The allergen asserts queried b.catalog_id, which the bridge table does not carry; the catalog id lives on recipes_catalog_lines. Join the lines to the bridge on ingredient_key and filter on the line's catalog id.
loadSnapshot calls SubstitutionService.substitutions(for:) from inside its own dbQueue.read closure, and the service re-entered the same queue through loadBridge's db.read — a GRDB reentrancy fatal that killed the macOS test runner mid-suite. Thread one caller-supplied bridge read through: loadSnapshot reads the bridge on its active db handle and the service accepts pre-read maps. Same data, one read, one consistent snapshot.
skipRemember reduced the session to remembered but never wrote the cook_remember receipt, so the journal could not distinguish an explicitly skipped remember step from a cook that never got one. Record the entry with skipped: true, mirroring saveRemember, and guard the phase like the save path.
loadSnapshot reads production v16's inventory_lots.quantity_is_estimate as the estimate authority; the weekly fixture's hand-rolled table lacked the column, so three snapshot tests failed with SQLite error 1.
GRDB's read takes a throwing block, and bridgeRows throws — without an inner try the macOS compiler rejects the call (x141's Linux harness stub did not compile the real service). Matches the file's existing read idiom.
The merge that moved the identity bridge to per-query reads changed offerableSwaps to take the bridged maps as a parameter and updated hasSubstitutions, but left the substitutions and screenedSubstitutions callers on the old three-argument shape, so build-and-test failed with "missing argument for parameter 'bridged' in call". Both now resolve the bridge through the service's held-connection bridge(). Co-authored-by: Sam Gu <sgu07966@gmail.com> Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
… load calls 86bcc38 pointed PlanDataAdapter.loadSnapshot at substitutions(for:dietaryRestrictions:in:) but never added the overload, so the build failed with "extra argument 'in' in call". The overload reads the identity bridge on the caller's held connection instead of re-entering the queue, which is what produced the "Database methods are not reentrant" fatals in the plan-persistence tests. The queue-based and held-connection entries now share one assemblingSubstitutions helper so legacy-entry mapping cannot drift between them. Root cause audited read-only against a deterministic Linux reproduction; patch shape from the audit (REPORT.md sec 4) adapted to the already-fixed callers on this head. Co-authored-by: Sam Gu <sgu07966@gmail.com> Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
927c062 taught CookReceiptLog to stamp receipt rows with the owning session (id + revision) for the FK, but this call site still passed only the store, so the app target no longer compiled. Pass the current session's id and revision, matching the aligned regression tests.
The join branch landed independent versions of the same four defect fixes (skipped-remember receipt, weekly fixture schema, held-connection bridge read, receipt-log session identity). Resolve the four conflicted files in favor of the join branch's reviewed implementations so the wiring PR merges cleanly; the wiring, rights filter, and GoalPath test are this branch's own work.
The merge run surfaced two schema gaps that earlier failures had masked: - GoalPathTests joined the catalog lines to the identity bridge on b.catalog_id, a column the bridge does not carry; filter the catalog id on the line side where it lives. - LeChefPlanWeeklyTests' hand-built fixture never created the production cook-plan tables, so adapter snapshot reads hit 'no such table: cook_plan_line' once the estimate column was added. Run the production CookSessionMigrations over the fixture, as the round-trip test already does.
Both failures were expectations written before the assertions could ever execute against real data: - GoalPathTests required the cook target to carry a thermometer check, but the shipped everyday-2026-10 bundle has zero non-null thermometer steps. Select the exact-tier pool entry with a timer on any step and advance the session to that step before firing startSuggestedTimer. - testCatalogPantryFixtureYieldsAFeasibleWeek ignored the substitutions bundle: the shipped reviewed table offers egg->tofu (binder, 1:1) for the bridged egg key, so the 100 g egg shortfall is covered by tofu and tofu's merged shortfall becomes 300 g. Verified against the real allocator in the Linux harness (bundleless: [2:100, 3:200, 4:200]) and against the macOS failure log (bundle: [3:300, 4:200]).
The cook target admits exact-tier pool entries whose timer sits on any structured step, so drive the session forward until the suggested timer is offered, then start it. Guards the advance loop so it can never trip complete() on the last step.
Two goal-path assertions described behavior the stack does not implement: - Pantry deduction: applyConsumption reads legacy recipe_ingredients by the resolved recipe id; a catalog pool entry materializes an ingredient-less legacy row, so a catalog cook deducts no stock. That is the inventory-deduction gap tracked as a feature-sized follow-up — pin the empty consumption result and unchanged total instead of asserting a drop. - Learned-layer evidence: the hidden layer's numerator counts cooks rated 2 stars or below, so a 4-star cook contributes denominator only. Assert the held low-rating claim by denominator, not numerator.
2 of 3 tasks
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.
test(ios): wire the overhaul into the app and prove the fridge-to-meal-to-taste path
Human author: Sam Gu (sgu07966@gmail.com)
What changed and why
The overhaul packets shipped working code with no construction sites in the app: the community client stack (PRs #159/#166/#173/#179), the taste-profile repository (PRs #165/#167/#176), and the LeChef plan sheet (PRs #152/#178) had no app-side wiring, and the catalog recommendation pool did not filter on rights status. This PR is the wiring join.
AppDependencies now builds the community stack once at launch —
CommunityStore,CommunitySessionService(keychain-backed token store),SessionViewModel,TablesViewModel,ChallengesViewModel— and constructsTasteProfileRepositoryagainst the same shipped-bundle feature index the residual ranker uses (one index, shared; previously two separate builds).makeTasteProfileViewModel()andmakePlanSheetViewModel()expose the profile and plan-sheet view models through the production adapters (TasteProfileRepository,PlanDataAdapter.loadSnapshot). No view references them yet — see the UI-work list below.Community backend URL resolves from the
COMMUNITY_BACKEND_BASE_URLenvironment variable or Info.plist key, defaulting to the local dev server (backend/community, port 8087). Deployments set the env or plist value.Rights gate:
catalogMatchesnow filtersWHERE r.rights_status = 'original', so a rewritten recipe can never reach Home's recommendation pool even if the shipped bundle ever carries one.GoalPathTests (new,
Tests/GoalPath/) walks one fridge-to-meal-to-taste path over a fresh on-disk database, shipped resources, and the realAppDependencies— no packet-era mocks:ingestConfirmedScanscan-review path (plus a mid-coverage staple set as a household allergen throughsaveHealthProfile).findMakeable/findNearMatchover the real inventory and profile, thentasteResidualRanking.applyexactly as the dashboard calls it; the rule order passes through unchanged (residual off), exact catalog matches surface, and no pool entry requires the allergen.CookSessionStore, realSubstitutionService, fail-closed allergen gate); the session runs the structured steps, starts the step's stated timer, and the thermometer check is present.proposal-timer_start.jsonfixture decodes throughLiveProposalFramesand is approved on the cooking session viasession.process; the receipt is.executedwith the recorded proposal id and payload hash, and the "Noodles" timer persists incook_timer.cooking_historyrow linked to the exact(catalog_id, revision), nutrition reads the frozen revision snapshot, pantry stock drops, and therecipe_cookedtaste event carries the catalog ref with the rating.cook_plan/cook_plan_dinner, and no planned catalog recipe uses the allergen ingredient.community_outboxwith the cooked recipe's catalog ref in its payload.UI work for Sam and Opus (not in this PR)
Per the join's scope, screens were not designed or modified. What now exists and is reachable from view models, but has no view:
deps.communityTablesViewModelanddeps.communitySessionViewModel.deps.makeTasteProfileViewModel()(sentences with evidence details, including hidden insights, plus the correction actions).deps.makePlanSheetViewModel().LiveAssistantViewModelalready exposes proposal cards).COMMUNITY_BACKEND_BASE_URL(env now, Info.plist later — aproject.ymlentry, deliberately not edited here).How it was checked
FridgeLuckTestsimplicitly through the repo's test plan;GoalPathTestsfollows the existingCatalogCookFlowTestsconstruction patterns (freshDatabaseQueue+DatabaseMigrations.migrate+@MainActorsuite).ingestConfirmedScan(detections:confirmedIngredientIDs:selectedIngredientByDetection:sourceRef:location:preserving:),findMakeable/findNearMatch,TasteResidualRankingService.apply(to:surface:shownCount:now:)withTasteSurface.homeTonight,CookModeViewModel.Dependencies(incl.substitutionOptions,NoopTimerSchedulerdefault fromephemeral),session.process(proposal:decision:deviceEvents:),MealLogService.logMeal(... sourceRefPrefix:catalogId:catalogRevision:) -> Outcome,catalogNutrition(forHistoryID:),TasteEventStore.events(),TasteStatementBuilderrender/denominator gates,PlanSheetViewModel.load/setDinnerCount/plan,PlanDataAdapter.save,CommunityStore.enqueue/queuedOutboxCount,SessionViewModel.isAdult.swiftc -parsewas not available locally; the CI build is the parse gate.2026-10-15T18:22:41Zsits inside the fixture's five-minute TTL.What could not be checked
Assumptions
http://127.0.0.1:8087/v1) —backend/community/src/config.tsdefaults its port to 8087 and the client contract wants the version prefix included. Real deployments must setCOMMUNITY_BACKEND_BASE_URL.samgu.FridgeLuck.communitywas chosen for the token store; no repo convention pinned one.logsMeal: falseas the guide wires it, and the GoalPath logs through the samedeps.mealLogService.logMealcall the celebration makes.Pre-existing failures fixed on this branch
The join base (
obv/burst8-fridgeluck-joinat 42c4b4f, run 38840349315) failed its fulltest phase with four distinct defects; all four are fixed here so CI can report green:
PlanDataAdapter.loadSnapshotcalledSubstitutionService.substitutions(for:)inside its owndbQueue.readclosure, and theservice re-entered the same queue through
loadBridge'sdb.read— GRDB's"Database methods are not reentrant" fatal, which crashed the macOS test runner
repeatedly (six restarts in one CI run). Fix:
loadSnapshotnow reads the bridge onceon its active
dbhandle and passes the maps through a newsubstitutions(for:dietaryRestrictions:bridged:)overload; the queue-reading API isunchanged for all other callers.
b.catalog_id; the catalog id lives onrecipes_catalog_lines. Join corrected tolines→bridge on
ingredient_key.CookModeViewModel.skipRememberreduced the session butnever wrote the
cook_rememberrow, failingtestCelebrationDismissalSkipsRememberAndPersistsTerminal(0 vs 1). It now records thereceipt with
skipped: true, mirroringsaveRemember.LeChefPlanWeeklyTests's hand-rolledinventory_lotslackedproduction v16's
quantity_is_estimate, failing three snapshot tests with"no such column". Column added to the fixture.
CI evidence
Evidence — CI green (2026-10-16, head e87ee7b)
e87ee7b2.GoalPathTests.testFridgeToMealToTasteGoalPathpassed (2.297 s): pantry scan-review ingestion → Home recommendations with the rights filter → structured cook session with timer-proposal approval → catalog-linked meal log with frozen nutrition → taste evidence → weekly plan → community outbox.LeChefPlanWeeklyTestspassed, including the swap-aware feasibility expectation (3:300.0,4:200.0with the reviewed egg→tofu substitution).applyConsumptionreads legacyrecipe_ingredients; the inventory-deduction gap stays a tracked follow-up), and the learned layer's numerator counts only low ratings, so a 4-star cook holds denominator-only hidden evidence below the render gate.🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact