Skip to content

[obvious-fleet] join: integrate the overhaul slices (burst5/6/8) into one branch from main - #181

Merged
SamGu-NRX merged 273 commits into
alphafrom
obv/burst8-fridgeluck-join
Oct 16, 2026
Merged

SamGu-NRX merged 273 commits into
alphafrom
obv/burst8-fridgeluck-join

Conversation

@obvious-autobuild

@obvious-autobuild obvious-autobuild Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Human author: Sam Gu (sgu07966@gmail.com)

What changed and why

This branch is the integration join: one branch cut from main that merges every overhaul slice of the burst5/6/8 waves, so the app, backends, data pipeline, and evaluation tooling can be verified and reviewed as one tree instead of twenty-five parallel stacks. The merges on the branch (each a merge: integrate ... commit) carry PRs #146–#180 lineage: the four spec/contract slices (#146 taste, #147 lechef, #148 recipes, #150 community), the taste store/lab/live slices (#155, #165, #167, #176), the LeChef cook/plan/tools/live slices (#152, #153, #156, #157, #171), the recipe pipeline/originals/integrate/bundle-v2 slices (#158, #162, #163, #168, #148/#151 hydration), the community client/groups/api/e2e/contract slices (#159, #166, #179), the catalog recommend/cook-flow/reach slices (#172–#175), the weekly plan (#178) and substitution canonical table (#180), plus the overhaul trunk (#170). No history was rewritten; every slice arrives as a merge of its reviewed PR head.

On top of the merges, this retry round repairs the four places where the join had drifted or failed:

  • c34b82a8 — TasteEventStoreTests.swift had a committed conflict marker from the trunk port; resolved in favor of the stronger assertions (each event asserted present, failing if either is missing).

  • 51f14e11 — the taste-store-check Linux mirror now pins the catalog feature index and its pure FeatureLogic inputs via a mirror-local bundle-URL shim, so the suite runs on Linux without pulling non-portable AppDatabase dependencies.

  • 5a82b961 — the taste-lab reference engine was still on pre-e95e2644 semantics and failed contract parity after the spec branch's per-kind payloads and evidence gates landed: the recipe_rated merge into an unrated cook now adds the rating as its own cook_rating evidence at the merge instant (instead of replacing the no-rating update), saves use reward 0.75 and skips 0.15 (new REWARD table matching the contracts checker), the per-recipe learnedContribution reports the clamp-bounded value with pins counted in, pins render alongside inferred statements (the old pinned-opposite suppression is gone), and the hedged wording rule requires numerator == denominator. Parity tests replay the contract fixture's seeded pin statements; the clamp test's unclamped-sum oracle sums per-feature contributions. The three golden household fixtures were regenerated over the merged bundle-v2 catalog.

  • edd5cc8d — evaluation/catalog-reach/reach-after.json|md regenerated for the merged state: the committed pair predated the final bundle regen (763 recipes, 461 original-rights, 0 legacy_id-resolvable keys) while the merged bundle ships 762 recipes (all original-rights), 49 legacy_id-resolvable keys, and 655/762 (86.0%) fully resolvable. reach-before is untouched.

  • 26871886 — the community-check migration mirror refresh (the two drifted source pins + HASHES.txt) committed after being verified locally in the previous round.

  • d5ad8965 — reconciled the parallel contract-alignment head (5a82b961): its engine, tests, and regenerated fixtures were adopted wholesale (its hedged rule — hedged only when numerator == denominator — is the faithful checker wording), the conflicts were resolved in its favor, and the one rule it had not ported was restored on top: profile staleness now decays on the feature's most recent evidence class (HALF_LIFE[post.last_class]), matching check.py's HALF_LIFE[final[fk][3]].

  • 137eed07 / 65928425 — the production Swift residual engine (apps/ios/FeatureLogic/Taste/Engine/TasteResidualEngine.swift) now reproduces the committed golden household fixtures: 137eed07 landed the ingest half (the rated-cook merge adds the rating as its own cook_rating evidence at the rating's instant with the cook's exposure share instead of replacing the no-rating update; saves apply reward 0.75, skips 0.15), and 65928425 reconciled it in a rebase and ported the remaining rank/profile half — rank() reports the clamped learned sum with pins counted as learnedContribution (the same number totalScore adds), inferred-statement staleness decays on the feature's most recent evidence class, hedged low-rating wording requires numerator == denominator below 10 opportunities, and inferred statements render alongside active pins (the pinned-opposite suppression is gone). testLateRatingMergesIntoCookWindow now pins the add-on-top merge arithmetic (alpha 1.295 / beta 1.03 for a 5-star rating over a 0.25-share cook), and the taste-store-check mirror pin was refreshed. Fixtures untouched — they are the spec.

  • 86bcc382 — fixed the snapshot-load reentrancy fatal behind the repeated GRDB "Database methods are not reentrant" crashes that killed LeChefPlanPersistenceTests runners: PlanDataAdapter.loadSnapshot() ran the reviewed-substitution loop inside its own dbQueue.read closure while SubstitutionService.substitutions(for:) opened a second read on the same serialized queue (bridge() → loadBridge(db: DatabaseQueue)). The service now has a substitutions(for:dietaryRestrictions:in:) overload that fetches the identity bridge on the caller's held connection (loadBridgeRows(db: Database)); the queue-based loader keeps its own transaction for standalone UI callers, and offerableSwaps takes the bridge as input so every path shares one read.

  • b16e0984 — the catalog integration test pinned the legacy omelette as the quick suggestion for a tomato-only pantry, which held while the near-match pool was legacy-only. Under mergedResults' shared ranking (score desc, missing count asc, time asc — no legacy preference), the regenerated 762-recipe bundle's catalog entries legitimately compete, and a catalog recipe with a stronger health score now tops the near match (its nil legacy id surfaced as "nil" because Recipe.id is Int64?). The omelette's presence and exact fit lines stay asserted; the quick-suggestion check now pins the published shape — quick suggestion equals the top near match when nothing completes — so it guards engine/UI consistency without pinning a bundle-specific winner.

The two repairs merged with the parallel taste-engine port (65928425) as head 8dc9329f; no file overlap, merge is clean.

  • Re-run at 8dc9329f (Linux, Swift 6.1.2): the temporary replay harness over the production TasteResidualEngine (byte-for-byte sources + swift-crypto shim) reports ALL FIXTURES REPLAY EXACTLY for all three golden household fixtures against the shipped 762-recipe catalog — confirming the merged engine (contract ingest + class-aware staleness) still reproduces the committed fixtures; the fixture files are untouched. Hosted iOS CI run 38844186301 runs on 8dc9329f.

How I checked it

All commands run in the sandbox at tree state d5ad8965 (the merge of the parallel alignment head into this branch; the round's re-runs are listed in the final bullet below):

  • taste-store-check (Linux, Swift 6.1.2): 50/50 passed (./refresh.sh mirror sync + suite run; shared slice now 15 tests).

  • community-check: 74/74 passed (bun source pins verified; Swift test linked with -Xlinker --allow-shlib-undefined — the swift::threading::fatal link gap is a Linux toolchain quirk, not a test failure).

  • Substitution checker: OK — 70 swaps, 126 mapped endpoints, 329 composition keys, sweep pass (check_substitutions.sh over the canonical-key table).

  • Python suites (pytest): substitutions + everyday tools 64 passed; data/recipes/tools 84 passed, 1 skipped (pre-existing skip).

  • evaluation/taste-lab (python3 -m pytest tests): 10/10 passed after the engine alignment + fixture regen.

  • Contracts (docs/spec/taste/contracts): check.py — all examples valid and self-consistent; mutation_matrix.py — all mutations caught, positive control passes.

  • backend/community: tsc --noEmit clean; bun test 138 pass / 26 skip / 0 fail (after bun install; without it the Bun type defs break typecheck — environment, not code).

  • backend/gemini-agent: tsc --noEmit clean; bun test src/__tests__ 338 pass / 0 fail; lechef suite (bun src/run/lechef-suite.ts) 65/65 job successes, 0 unsafe answers, 0 unauthorized actions, all gates true.

  • evaluation/catalog-reach: python3 reach_report.py --repo-root ../.. --out ... --md ... reproduced the committed numbers after regen (86.0% fully resolvable).

  • Re-run on the merged head d5ad8965: evaluation/taste-lab pytest 10/10 passed and python3 -m taste_lab validate-fixtures reports All fixtures replay exactly; community-check verification script clean and swift test 74/74 on the committed mirror refresh (same invocation flags as before).

  • Re-run at 65928425 (Linux, Swift 6.4, no Xcode): a scratch package mirroring the production sources byte-for-byte (TasteResidualEngine, TasteEngineModels, TasteFeatureExtractorV2, TasteFixture, RecipeBundleV2; CryptoKit→swift-crypto shim, the taste-store-check pattern) replays the three golden household fixtures through the TasteRankerTests comparison loop — ranking order, per-feature contributions, learnedContribution, totalScore, profile and hidden statements — 7/7 tests passed (swift test, incl. testGoldenFixturesReplayExactly); evaluation/taste-lab pytest 10/10 passed; docs/spec/taste/contracts/check.py 19/19 examples valid; taste-store-check/Scripts/refresh.sh mirror in sync at the new engine pin (0b4a34b3…).

What I couldn't check

  • No Xcode in this sandbox: the iOS app surfaces compile and run their XCTests only on the repo's hosted macOS CI, which runs on this PR's push. Linux swift test in FLFeatureLogic has a pre-existing no such module CoreGraphics failure, which is why the portable mirrors exist.
  • reverse-meal-eval was not run (documented as unrun in the prior rounds of this join).
  • The two repair commits I pushed before opening this PR (5a82b961, edd5cc8d) are authored as Obvious <obvious@obvious.ai> without the Co-authored-by: Sam Gu trailer — adding it would require rewriting pushed history (force-push), which is not allowed; attribution is recorded here.

Assumptions

  • reach-after.json|md are generated artifacts that track the merged catalog state, so refreshing them on the join is correct; reach-before stays pinned to the pre-join baseline.

  • The engine keeps a stray-late-rating correction path even though the contracts checker raises on re-rating an already-rated cook (in the app, corrections arrive as correction_applied events, which the checker ignores); the golden fixture does not exercise that path.

  • .github is kept identical to the trunk branch (f6f33e2a) so this PR's CI matches the trunk's hosted macOS workflow.

  • The PR targets main because the branch was cut from the main tip; no earlier PR ever existed for obv/burst8-fridgeluck-join (open or closed), so this is the join's first PR.

  • Round result: CI run 38845246311 on 34d74e12 compiled cleanly (8m36s — past every earlier compile failure) and ran the suite with zero GRDB reentrant fatals — the snapshot reentrancy fix holds. Three LeChefPlanWeeklyTests tests then failed with no such table: cook_plan_line: the hand-built makeDatabase() fixture never creates the production cook-plan tables, which the now-working snapshot read touches. The parallel thread repaired the fixture standalone as fb0f868b (production CookSessionMigrations.migrate in the fixture — the same repair its PR test(ios): wire the overhaul into the app and prove the fridge-to-meal-to-taste path #182 carries for the GoalPath slice); that is the current branch head, with run 38846607091 in progress. A local goal-path merge that duplicated the fixture fix was discarded in favor of the shared minimal repair; PR test(ios): wire the overhaul into the app and prove the fridge-to-meal-to-taste path #182 lands through its own gate.

  • Queue-boundary caveat (recorded as agreed): the reentrancy refactor is syntax-checked locally (swiftc -parse) but typed and tested only on hosted macOS CI — -parse does not catch missing-argument errors, which is exactly how two bridged: arguments slipped through to run 38844186301 and were fixed in ead103e1/7456f537.

  • Round result: CI run 38846607091 on fb0f868b fixed the missing-table failures but exposed one stale expectation: testCatalogPantryFixtureYieldsAFeasibleWeek pinned the pre-substitution shopping list ["2:100.0", "3:200.0", "4:200.0"], while the snapshot now legitimately carries the shipped reviewed egg→tofu binder swap (substitutions-v2.json egg-binder-to-silken-tofu, ratio 1.0) bridged to legacy ids 2→3 — the planner covers egg's 100 g shortfall with tofu, yielding ["3:300.0", "4:200.0"]. That swap was the first to bridge cleanly because egg and tofu both resolve in the fixture; the pre-reentrancy-fix trees never reached this path (fatal) or lacked the bridge rows. The parallel thread repaired the test as 1c22f76b — updated list plus a positive assertion that the reviewed 2→3 swap actually rides the dinner lines — and run 38847597335 is in progress on that head. Diagnosis independently reproduced from the shipped swap JSON and fixture bridge before adopting the shared fix.

  • Round result: CI run 38847597335 on 1c22f76b is fully green — build-and-test (5m40s) and dead-code-advisory (3m55s) both pass; 666 tests executed, 1 skipped, 0 failures, zero GRDB reentrant fatals across the whole suite. The join branch compiles the merged overhaul stack and passes every weekly-plan, catalog, cook-session, and GoalPath-adjacent suite on hosted macOS. PR [obvious-fleet] join: integrate the overhaul slices (burst5/6/8) into one branch from main #181 is marked ready for review on branch head 1c22f76b; merge is the human's (squash per repo policy).

🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact

🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact

🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact

CI round — weekly-plan fixture (fb0f868)

  • Run 38845246311 on 34d74e1 compiled cleanly and ran the suite with zero GRDB reentrant fatals — the held-connection bridge repair held through a real test run. Three weekly-plan tests failed: SQLite error 1: no such table: cook_plan_line — the catalog adapter tests build their database through the hand-rolled makeDatabase() fixture, which never created the cook-plan tables that the snapshot load reads for plan reservations (PlanDataAdapter reservations query).
  • Fix (fb0f868, test-only): the fixture now runs production CookSessionMigrations after the core schema, mirroring what a real app database carries. Verified swiftc -parse clean; the round-trip test's explicit migrate call stays and is idempotent.
  • Taste-parity and GRDB verification: Linux-side suites green earlier (taste-lab 10/10, contracts 19/19, store mirror 50/50); hosted macOS test verdict pending on the next run.

CI round — swap-aware feasibility expectation (1c22f76)

  • Run 38846607091 (fb0f868): zero GRDB reentrant fatals, zero compile errors, 15/16 weekly tests green. One remaining failure: testCatalogPantryFixtureYieldsAFeasibleWeek expected [2:100, 3:200, 4:200], got [3:300, 4:200].
  • Root cause: the expectation predates swap semantics. The shipped reviewed v2 table carries egg-binder-to-silken-tofu (binder ratio 1.0, plausible verdict and recheck); the allocator covers a short required line only when the substitute covers the shortfall fully ("no half-swaps"), so the egg 100 g shortfall rides the swap into tofu — egg never reaches the list, tofu's buy grows by exactly the shortfall. Production verified correct against PlanAllocating.allocateDinner and the bundle JSON; the test expectation was stale.
  • Fix (1c22f76, test-only): expectation updated to the post-swap buys, plus an explicit assertion that the egg line carries a reviewed egg→tofu swap. swiftc -parse clean.

CI round — green (1c22f76)

  • Run 38847597335 on 1c22f76: build-and-test success. Executed 666 tests, with 0 failures (0 unexpected); Test Suite 'All tests' passed; zero Database methods are not reentrant fatals; the previously failing testCatalogPantryFixtureYieldsAFeasibleWeek passed with the swap-aware expectation.
  • Status: ready for human review and merge (humanMerge policy — no autonomous merge).

Greptile review round — 5542992530 verification record

All six findings were verified against the source at 1c22f76. Every one is real. The PR merged (into alpha, merge commit 485ae82e) before any fix or test landed on the branch, so none of the six is fixed in this merge; the verdicts below come from code reads (file:line at 1c22f76), not from failing tests.

  • Finding 1 — catalog guides open empty (CookingGuideView.swift:63): confirmed. Catalog results materialize Recipe(id: nil, instructions: "") (RecipeRepositoryCatalogRecommend.swift:138-148), and the guide resolves catalog content only through the legacy-id bridge (catalogRecipeID(forLegacyRecipeID:)), so Start Cooking on a catalog-native result opens a guide with zero steps and zero ingredients; RecipeResultsView.handleStartCooking passes the raw result through. A reviewed fix exists on burst9 (commit 770ab5eb: makeCatalogContent resolves the packet through scoredRecipe.catalog?.ref.id, with the legacy bridge as fallback, and steps/ingredients load from it) — inbound via PR feat(release): the release-candidate build with every late fix, the goal path and the safety sweep green #188.
  • Finding 2 — catalog meals lose nutrition (MealLogService.swift:99): confirmed (journal, deductions, and a dead reader). For a catalog-native cook, resolvePersistedRecipeID inserts a header-only recipes row (no same-title legacy row to match), so computeConsumedMacros (journal) and consumptionRequests (inventory deductions) — both reading recipe_ingredients — see zero grams, and catalogNutrition(forHistoryID:) has no production caller. The frozen-snapshot journal read is fixed in 770ab5eb (PR feat(release): the release-candidate build with every late fix, the goal path and the safety sweep green #188). The deduction half — consuming the packet's bridged lines through applyConsumption — has no fix on any branch; it is feature-sized and left open.
  • Finding 3 — timer alerts stop matching (CookModeViewModel.swift:305): confirmed; open on every branch. resumeTimer and adjustTimer pass label: nil; perform schedules only if let label; pausing emits the notification cancel, so a resumed timer re-arms nothing and an adjusted timer keeps the stale deadline. The label lives in the store record (CookTimerStore), so the fix is to pass it into perform. The view model imports UserNotifications, so a fix plus a scheduler-spy test validate on hosted macOS CI only, not the portable Linux harness.
  • Finding 4 — dinner stock counted twice (PlanAllocating.swift:477-483): confirmed within one dinner. Each line reads the unchanged pool while a swap in the same dinner consumes from the same pool; reservation happens only after the whole dinner is picked, so a swap-consumed substitute and a direct line of one ingredient can both claim the same grams. Cross-dinner reservation is correct (the weekly fixture proves it). A reviewed fix exists on burst9 (a per-dinner claimed map every line reads against) — inbound via PR feat(release): the release-candidate build with every late fix, the goal path and the safety sweep green #188. A fails-first portable test (one dinner whose short required line swaps into an ingredient the same dinner also consumes directly; total consumed never exceeds pool stock) was designed but not committed here.
  • Finding 5 — saved swaps reserve wrong stock (PlanDataAdapter.swift:280-282): confirmed; open on every branch. Swap-covered lines are saved under the original ingredient with the full requirement and no shortfall, and loadReservedGrams selects no swap column, so it reserves the full requirement against the original while the substitute that actually covered the shortfall reserves nothing and stays double-bookable. Fix shape: persist the consumed split on PlanSwap (optional originalGrams/substituteGrams, back-compatible through the wholesale swap JSON codec) and reserve the split on load, with the legacy math as the fallback for old rows.
  • Finding 6 — catalog ignores keto (RecipeRepositoryCatalogRecommend.swift:94): confirmed. The diet gate handles vegetarian and vegan only; keto maps to the .lowCarb mask on the legacy pool while RecipeCatalogClaims has no low-carb field, so no catalog recipe can prove compliance yet all of them pass the gate. The burst9 fix fails the gate closed for keto (case "keto": continue) — inbound via PR feat(release): the release-candidate build with every late fix, the goal path and the safety sweep green #188.

Round outcome: six of six findings confirmed; zero fixed in this merge. Findings 1, 2, 4, and 6 land with PR #188 (burst9 rejoin, base alpha). Findings 3 and 5 have designed fix shapes but no fix on any branch. No commits, tests, or CI runs were produced for this round — nothing was pushed to the branch, so there is nothing to run them against.

SamGu-NRX and others added 30 commits October 11, 2026 21:42
7e946b2 reports a missing reading only when the step names a thermometer,
so the metadata-only planted case now gets one message, not two. 7e946b2
was pushed with this test failing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ntradict

Second review: composition.py erased "peanut butter", "almond milk" and
similar phrases from the step text before every claim scan, so a nut-free
claim survived "serve with peanut butter". The exemptions are now per
claim: "peanut butter" is exempt from the dairy-free scan and still fails
nut-free. Compound foods named in a step block each claim they contradict
(pesto: nuts and parmesan; mayonnaise: egg; Worcestershire: anchovy and
malt vinegar; fish and oyster sauce; teriyaki, hoisin, miso, gochujang:
wheat; custard: milk; marzipan and praline: nuts). None of the 65 kept
claims changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hart

Second review: food_safety.py made venison a 145 F whole cut, which the
federal chart does not say. Each class pattern now cites the chart row it
encodes, and an animal food with no row (venison, bison, rabbit, goat,
game birds, organ meats other than poultry giblets) makes the gate refuse
the recipe instead of guessing. Ham takes 165 F because the name does not
say whether it is uncooked or reheated. No corpus recipe uses any of these.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Second review: rights_evidence.py accepted an originals citation by its
spelling and an OGL record with any two HTTPS URLs, so a nonexistent record
with an id- and SHA-shaped string passed.

pin_rights_evidence.py writes data/recipes/evidence/rights.json from git:
each authoring record at overhaul/original-recipes 607ef99 with its recipe's
title and a hash of its ingredient names, plus the terms each external
source was checked against, with page hashes and quoted sentences. The gate
now looks the cited record up at the cited commit and checks licence,
permissions and recipe identity; an external source clears only with a
pinned "verified" verdict. A test re-derives the evidence file from git.

Checking the NHS terms changed the verdict. The two NHS Healthier Families
recipes link their own Terms of Website Use, fetched 2026-10-11: "All such
rights are reserved" and "You must not use any part of the content on the
Site for commercial purposes without obtaining a licence". The records cite
the general nhs.uk OGL release instead. With the terms in conflict the
permissions are unknown, so fl-nhs-0001 and fl-nhs-0002 move to
quarantine.jsonl; the corpus is 401, one over the 400 bar.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Totals, the suite C table, section 2 and section 10 now cover the second
round: pinned rights evidence and the NHS quarantine (27 quarantined, 401
shipped, one over the 400 bar), affirmative thermometer readings, per-claim
step exemptions, chart-row classes and the soft-yolk rule. The resolution
gap is 2612/2829 (92.33%), short 76.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Register four subsystem migration files (RecipeCatalog, Taste, CookSession,
Community) after the core v18 migration so parallel branches can add local
tables without editing Migrations.swift. Registration order is fixed because
shipped databases record applied identifiers and subsystem migrations may
reference core tables and earlier subsystems. Structural seam tests pin the
contract: full migrate, upTo v18 then finish, identifier classification, and
block ordering.
applied[.. < firstSubsystem] parses as a comparison, not a half-open range,
which failed the CI build. prefix(_:) and suffix(from:) express the same
slices without operator spacing pitfalls.

Co-authored-by: Obvious Autobuild <agents@obvious.bot>
…chine-checked contracts

Rule-anchored residual ranker spec (SPEC.md), JSON Schema contracts for
taste events, exposures, profile statements, and golden fixtures, plus 17
validated examples, a stdlib checker that re-runs the reference model over
the golden fixture at 6-decimal precision, and the example generator.

check.py: 17 examples valid and self-consistent (exit 0); planted-failure
negative test exits 1.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…seam' into obv/burst5-fridgeluck-taste-store
Five local-first jobs (plan, substitute, coach, adapt, remember) with
dedicated surfaces, chat as fallback, and a phone-authoritative
proposal/receipt model adopting PR #102's wire contract verbatim.
Substitutions derive role-scoped reviewed swaps from PR #114's pairs.json
with fail-closed rechecks; planning merges #52/#141/#142 over one stock
pool; privacy adopts #26's frame-cache and session-expiry findings.

contracts/: six JSON Schemas, eight fixtures, check.py (20 checks green:
schema validation, 300s TTL, payload-hash round-trip over real SHA-256
digests, plan pool conservation, nine-class allergen gate).

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…t contracts

Machine-checked contract layer for the recipe system: canonical ingredient
identity over the 50 legacy data.json ids (13 resolved, 10 proposed, 27
unresolved against the bundled USDA catalog - nothing invented), a strict
JSON Schema for shipped recipe bundles v2 (quantities fail closed, pinned
thermometer checks, null-able diet claims, nutrition coverage bands with
basis, rights provenance, stable catalog ids), and the taste-signal event
envelope feeding on-device learning. Includes a 6-recipe sample covering
every quantity kind and coverage level, generated fixtures, check.py
(7/7 tamper cases caught), and SPEC.md documenting decisions and
alternatives.
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
User recipes, Explore, challenges and battles, Tables, identity/safety,
backend/client architecture, mocks in words, taste events, and three
implementation slices, drafted on top of the overhaul SPEC.

Contracts: 24 JSON Schemas, 33 operations, 31 example fixtures, and a
stdlib-only check.py that validates every example against its schema,
manifest coverage, and OpenAPI/manifest sync (exit 0).

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
… simulator, and offline ranking evaluation

Deterministic Python reference implementation of the taste-residual-v1
learning model from docs/spec/taste/SPEC.md (PR #146), plus a seeded
household simulator and a four-arm offline evaluation:

- taste_lab/engine.py: lazy-decay Beta posteriors per feature key,
  exposure-share weighting, cook-transaction windows, profile statements
  — byte-exact with docs/spec/taste/contracts/check.py:recompute() on the
  committed coriander-v1 golden fixture (tests/test_parity.py), extended
  with documented decisions for the event kinds the checker's cook-only
  reference does not exercise
- taste_lab/rules.py: faithful port of PersonalizationService +
  RecipeScoring ranking (the production rule anchor)
- taste_lab/sim.py: seeded households with latent preferences; golden
  fixture writer
- taste_lab/baselines.py, metrics.py: residual / rule / profile-only /
  household-habit arms; NDCG@10, flippable-pair accuracy, churn, paired
  bootstrap CIs
- committed golden fixture (replay-verified) and 40x30-day, 40-household
  campaign report: residual vs rule +0.0059 NDCG@10 (95% CI
  [+0.0002, +0.0139]), 4% top-10 churn; popularity arm flagged as
  simulator leakage, not a recommendation

Checks: python3 -m pytest tests -q (8 passed); python3 -m taste_lab
validate-fixtures (replay exact); docs/spec/taste/contracts/check.py
(17 examples valid); pyflakes clean apart from intentional __init__
re-exports.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…seam' into obv/burst5-fridgeluck-taste-ranker
Review round (0 blockers, 8 majors, 4 minors, 2 nits), all addressed:
- renumber sections gap-free ascending (challenges 4, tables 5,
  identity 6, backend 7, app 8, mocks 9, events 10, slices 11) and
  re-point every cross-reference, including contract schema descriptions
- vote path in prose matches the contract (PUT /v1/challenges/{id}/vote
  with entryId; DELETE = removeVote)
- Explore candidate cap stated consistently (500 server-side before
  ranking, max 100 cards per response)
- outbox gains the report operation so offline reports are never dropped
- OpenAPI now declares error responses (422/429/503 publish, 409
  entries/tables/votes, 429 reports, 403 moderation) against error schema
- quarantined_rights defined in prose (rights re-verification on reopen)
- FitLine.state extended to the overhaul's five fit states
- evidence appendix cites real refs (PR #104/#106 branch heads, stage-0
  planning path)
- moderation decision path, RecipeRef community value, invite-mock
  section ref, and publication-state spelling note corrected

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…revision history

Adds the recipe-catalog persistence layer: subsystem migrations behind the
recipes_ identifier seam, a bundle-v2 loader with semantic validation, and
read paths on RecipeRepository.

- RecipeCatalogMigrations: recipes_catalog (+ nutrition snapshots, structured
  lines, structured steps), recipes_catalog_ingredients vocabulary; thermometer
  columns move as all-or-nothing; nutrition history is insert-once per revision.
- RecipeBundleV2Loader: applies recipes-v2.json when the bundle ships it, else
  hydrates legacy data.json into catalog rows keyed rcp-NNNNNN by legacy id
  (revision 1, quarantined rights, grams-only lines). v2 recipes claim their
  legacy successor only when the numeric suffix names a bundled recipe with a
  matching title, so cooking history keeps resolving without false links.
  Idempotent on re-apply; user-edited rows never overwritten; historical
  nutrition preserved byte-for-byte across revision bumps.
- RecipeRepositoryCatalog: catalogRecipe/catalogRecipes reads, legacy-id
  bridge, and per-revision nutrition snapshots.
- AppDatabase warmup applies the catalog after core hydration.
- RecipeLoaderTests: fresh install, v18 upgrade with cooking history,
  idempotence, revision bump, user-edit survival, loud failure naming the
  recipe id (malformed recipe, unpinned thermometer source, wrong bundle
  version), and read-path coverage against the spec example bundle embedded
  verbatim.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
One pure allocation pass over a value-semantic stock pool: FIFO-by-expiry
lots, duplicate-lot coalescing with deterministic tie order, per-dinner
reservation so stock is never counted by two dinners or two lines, the
five pantry-truth line states (PR #86 §3.2), 3x-weighted use-soon scoring,
full-cover-only substitutions, hard allergen filtering, and a merged
per-ingredient shopping shortfall. Same inputs always produce the same plan.
Frozen-snapshot recompute from a cached PlanSheetData, dinner count 1-7,
per-dinner servings, swap-one-dinner by exclusion, regenerate with honest
fallback, empty-kitchen and sparse-kitchen states — no views, logic only.
cook_plan/cook_plan_dinner/cook_plan_line frozen decision records with an
is_required column added to the §11 list so required short lines are
distinguishable from optional ones. PlanDataAdapter reads recipes, lots,
pantry assumptions, and household allergens in one snapshot (recognition
confidence never confirms quantity; below-threshold lots taint as
estimates) and saves/loads plans. Migration registration with the shared
app migrator is described in CookSessionMigrations and left as the one
outstanding wiring step.
Allocator: determinism, use-soon-first ranking, expiry-tie stability,
reservation no-double-count across dinners and lines, the five line
states, serving scaling, full-cover-only substitutions, hard allergen
filter, optional lines, honest empty/sparse outcomes.
Persistence: migration application and check constraints, plan save/load
round trip across all line states, idempotent replace, cascade delete,
and snapshot reads of candidates/lots/staples/allergens.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…seam' into obv/burst5-fridgeluck-community-client
Pure, public feature logic for cooking sessions (LeChef spec): bundle-v2
steps, a Codable session reducer with device effects, absolute-deadline
timers with clock-jump handling, an offline voice-command parser, and
the scaling engine's linear default with its reviewed exceptions.

45 XCTests cover transitions and replay, timer persistence, the five-word
command vocabulary with timer grammar, and the quantity/time scaling
tables. All green on Linux (Swift 6.1.2) via a scratch SwiftPM package;
the real iOS build is left to hosted macOS CI.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Adopt PR #114's reviewed substitution table (279 pairs) as bundled app
data, mapped to app ingredient IDs without guessing:

- scripts/substitutions/convert.py: pairs.json -> app-facing resource.
  Maps endpoints by exact name, alias, or fdc id only (no fuzzy match);
  ambiguous evidence fails closed; pairs with unmapped endpoints are
  dropped and counted in a mapping audit. Role fits carry per-role
  evidence verdicts (verified/plausible/unknown/unsupported; unknown is
  never upgraded); supported_also roles never inherit the primary ratio;
  conditional fails ride separately from functional fails. Recheck
  payloads carry the substitute's full compound allergen/diet closure
  and compositionKnown from the canonical facts store. 24 tests.
- apps/ios/Resources/Substitutions/substitutions-v1.json: generated
  resource (35/279 pairs, 40/317 ingredients mapped, 244 dropped
  fail-closed) from overhaul/substitution-table at 4b58954.
- apps/ios/FeatureLogic/Substitution/SubstitutionTable.swift: pure-logic
  lookup and per-eater composition screen (allowed / ask-user on unknown
  composition / refused with offending classes).
- apps/ios/Platform/Persistence/Services/SubstitutionService.swift:
  loads the bundled table; hardcoded 24-pair map removed; existing API
  kept, screenedSubstitutions added for ask-user states.
- apps/ios/Tools/substitution-check: portable check compiling the
  production logic against the generated resource (schema gate, audit
  counts, determinism, all-pairs x all-restrictions sweep, compound
  refusals, verdict honesty).
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…alidation

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Swift cannot synthesize Equatable for a struct holding a tuple field, so
the conformance failed the macOS build. No caller compares allocations;
drop the conformance.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
… parity tests

Swift port of the taste-lab reference engine under FeatureLogic/Taste/Engine/:
posterior model with lazy decay per evidence class, exposure-share weighting,
rated-cook transaction windows, GLOBAL_CLAMP ranking, and the inferred/pinned
statement layer with UUIDv5 statement ids (CryptoKit SHA-1). Feature keys come
from a deterministic extractor over the bundled positional data.json that
mirrors NutritionService.macros(for:) semantics.

Adds two generated golden fixtures (golden-hh01, golden-hh23; validate-fixtures
passes all three) and TasteRankerTests, which replays every committed fixture
through the Swift engine and compares ranking order, f6-rounded scores, per-
feature contributions, and profile/hidden statements byte-for-byte, plus unit
checks for cold-start no-op behavior, skip-outside-slate (SPEC 9), the
exposure floor, GLOBAL_CLAMP, the rated-cook merge, and shuffled-input
determinism.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: de0ea2a7-1f43-4957-8745-953dccfc964e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

SamGu-NRX and others added 20 commits October 16, 2026 01:49
… obv/burst8-fridgeluck-join

# Conflicts:
#	evaluation/taste-lab/taste_lab/engine.py
#	evaluation/taste-lab/tests/test_lab.py
#	evaluation/taste-lab/tests/test_parity.py
The merge of the receipt-FK slice (CookReceiptLog requires sessionID and
sessionRevision; receipt rows foreign-key to cook_session) with the
view-model lineage left the constructor call in CookModeViewModel.process
without the required arguments — the only compile error in CI run
38837169223. Pass the view model's live session id and revision; an idle
session has an empty id, which record() refuses loudly (noActiveSession)
instead of writing an orphan receipt (SPEC §8).
…to the join

Hosted macOS CI run 38838436665 failed the test targets on two more join
drifts, both caught only on Apple hosts (the portable suites do not build
the app targets):

- CookReviewFixRegressionTests expects CookModeViewModel.lastPersistError
  and the foreign-session abandonment from the #157 review round
  (927c062, 'abandon foreign sessions on start and surface receipt
  errors'); the join's merge of startOrResume() kept the pre-review body
  that silently orphaned a different recipe's still-cooking session. Port
  the reviewed declaration and abandon block onto the catalog-aware
  startOrResume() — at most one session cooking, failed persists surfaced
  (LeChef section 4.2).
- CatalogCookFlowTests built a hand-reviewed unknown-substitute offer with
  the pre-#180 Substitution memberwise init; the canonical-key round added
  the required substituteKey field, whose documented hand-built value is
  the empty string.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
1b9967e declared lastPersistError and fixed the hand-built offer key,
but nothing on the join writes the member yet: the pre-review startOrResume
body neither yields a foreign still-cooking session nor cancels its timer
notifications, and persist()/saveRemember still swallow failures with try?,
so the regression assertions pass vacuously. Port the reviewed block from
927c062 onto the catalog-aware startOrResume (abandon the foreign session,
persist it, cancel its notifications, surface write failures) and give
persist()/saveRemember the same do/catch surfacing with clear-on-success.
The merged executor derives requiredApproval through matchingDeviceEvent,
which reads instance state (the target's snapshot revision and consumed
device-event ids), so the type-level call from the fixture test no longer
compiles. Route it through the same makeExecutor() helper the sibling
receipt tests already use; the meal_log assertion is unchanged.
skipRemember() advanced the reducer but never wrote the skipped
CookRememberEntry, so the celebration-dismissal regression saw zero rows
where the reviewed lineage records exactly one (6af5be9). Restore the
recording with the same do/catch surfacing as the save path.
The rated-cook merge now adds the rating as its own cook_rating evidence
at the rating's timestamp with the cook's exposure share, on top of the
unrated cook's own no-rating evidence (SPEC 2.3 recompute model), instead
of replacing it. Save and skip evidence use the contract REWARD table
(0.75 / 0.15) instead of hardcoded 1.0 / 0.0.

Verified: a Linux Swift 6.1.2 replay harness over the shipped 762-recipe
catalog now replays all three taste-lab golden fixtures exactly.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
skipRemember() now records a CookRememberEntry with skipped: true through
the session store and surfaces write failures via lastPersistError, so an
explicit skip is taste evidence rather than silence and the regression
test sees one receipt.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The hand-built inventory_lots fixture in LeChefPlanWeeklyTests predates
production v16, so the PlanDataAdapter's estimate-taint query failed on
the missing column. The column mirrors Migrations.swift v16 exactly
(.boolean, notNull, defaults false).

Co-authored-by: Sam Gu <sgu07966@gmail.com>
… obv/burst8-fridgeluck-join

# Conflicts:
#	apps/ios/Feature/CookMode/CookModeViewModel.swift
…residual engine

The golden household fixtures were regenerated against the lab engine after
its alignment with docs/spec/taste/contracts/check.py (5a82b96); the
production Swift engine never received that port, so learned contributions
and the downstream ranking diverged (golden-hh00 rcp-000439 technique:simmer
0.006651 vs 0.009974 expected, ranking order flipping after the sixth
position).

Ported semantics, each mirroring check.py / taste_lab/engine.py:
- rated-merge into an unrated cook goes through the standard reward-update
  path (cook_rating, strength 1.0, the cook's exposure share, at the
  rating's timestamp) instead of replacing the no-rating update, so the
  evidence timestamp moves to the rating instant;
- recipe_saved applies the contracts reward 0.75 (was 1.0) and
  recipe_skipped 0.15 (was 0.0);
- rank() reports the clamped learned sum (pins included) as
  learnedContribution - the same number totalScore adds - instead of the
  raw sum;
- inferred-statement staleness decays on the feature's last evidence class,
  not a fixed cook_rating half-life;
- hedged low-rating wording requires numerator == denominator below 10
  opportunities; a mixed history uses the confident template;
- inferred statements render alongside active pins (the pinned-opposite
  hiding block is gone).

testLateRatingMergesIntoCookWindow now pins the add-on-top arithmetic
(alpha 1.295 / beta 1.03 for a 5-star rating over a 0.25-share cook).
Fixtures untouched - they are the spec.

Verified on Linux (no Xcode): a scratch package mirroring the production
sources byte-for-byte replays golden-hh00/hh01/hh23 exactly through the
TasteRankerTests comparison loop (7/7 tests pass); the lab engine replays
the same fixtures exactly; pytest 10/10 and contracts check.py 19/19 stay
green; the taste-store mirror pin is refreshed.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
…shot load

loadSnapshot() ran the reviewed-substitution loop inside its own dbQueue.read
closure, and SubstitutionService.substitutions(for:) opened a second read on
the same serialized queue through bridge() -> loadBridge(db:) — a GRDB
'Database methods are not reentrant' fatal that repeatedly killed the
LeChefPlanPersistenceTests runner.

The service gains a substitutions(for:dietaryRestrictions:in:) overload that
fetches the identity bridge on the caller's held connection
(loadBridgeRows(db:)); the queue-based loadBridge keeps its own transaction
for standalone UI callers, and offerableSwaps now takes the bridge as input.
The adapter's snapshot loop uses the in-transaction overload.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
…nner

The tomato-only-pantry assertion expected the legacy omelette as the quick
suggestion, which held while the near-match pool was legacy-only. The merged
762-recipe bundle's catalog entries share the same unified ranking
(mergedResults: ranking score desc, missing count asc, time asc) with no
legacy preference, so a catalog recipe with a stronger health score now tops
the near match; its nil legacy id surfaced as 'nil' in the old assertion.
The omelette's presence and exact fit lines stay asserted above; the
quick-suggestion check now pins the published shape: it equals the top near
match when nothing completes.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
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>
86bcc38 changed offerableSwaps to take the bridge as input but updated only
the in-transaction path and the adapter; the two queue-based conveniences
(substitutions(for:), screenedSubstitutions(for:)) still called it without
the bridged argument, which failed hosted CI with 'missing argument for
parameter bridged' at SubstitutionService.swift:151/:176. Both now load the
bridge through the queue-based loader, matching hasSubstitutions.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
…audited completion

Both threads fixed the two queue-based swap callers that 86bcc38 left
without the bridged argument, and the parallel completion adds the shared
assemblingSubstitutions helper on top. Adopted the audited remote file
(5d78868 + 7456f53): exactly one held-connection substitutions overload,
one assembly helper, and bridged: bridge() at all three queue-based
callers. Content is identical for the overlapping lines.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
CI run 38845246311 compiled cleanly and ran the suite with zero GRDB
reentrant fatals, but three weekly-plan tests failed with
"no such table: cook_plan_line": the catalog adapter tests build their
database through the hand-rolled makeDatabase() fixture, which never
creates the cook_plan/cook_plan_dinner/cook_plan_line tables the snapshot
load reads for plan reservations. The fixture now runs the production
CookSessionMigrations after the core schema, mirroring what a real app
database carries; the explicit migrate in the round-trip test stays and
is an idempotent no-op.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
… fixture

CI run 38846607091 (fb0f868) left one failure: the weekly feasibility
test expected [2:100, 3:200, 4:200], written before weekly shortfalls
could be swap-covered. The shipped reviewed table contains an egg→tofu
binder pair at ratio 1.0 (plausible verdict, plausible recheck), and the
allocator deliberately covers a short required line's shortfall only
when the substitute covers it fully — no half-swaps. Observed shopping
list [3:300, 4:200] is therefore correct: the egg 100 g shortfall rides
the swap into tofu, so egg never reaches the list and tofu's buy grows
by exactly the egg shortfall. The expectation now pins the swap as a
reviewed egg→tofu line swap and the post-swap buys.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
@codemetrics-ai

Copy link
Copy Markdown

Note

Review skipped

Codemetrics is installed on this repository, but it hasn't been added to your team yet.

To enable AI code reviews for this repo, go to your dashboard and add FridgeLuck to your team's repositories.

@greptile-apps

greptile-apps Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 0/5

[High impact] Integrates multiple feature branches with broad app-layer changes.

Fix the catalog cooking and logging paths, timer alerts, substitute stock accounting, and keto filtering before merging.

Fix All in Claude CodeFindings

  1. P1 Catalog guides open empty ▶
  2. P1 Catalog meals lose nutrition ▶
  3. P1 Timer alerts stop matching ▶
  4. P1 Dinner stock gets counted twice ▶
  5. P1 Saved swaps reserve wrong stock ▶
  6. P1 Catalog ignores keto choice ▶
Fix with agent prompt
### Issue 1
apps/ios/Feature/Recipe/CookingGuideView.swift:59-63
Catalog results have `recipe.id: nil` and empty `instructions`. This lookup requires a legacy integer ID, while the guide also reads its steps from those empty instructions and loads ingredients only through that ID. Selecting “Start Cooking” therefore opens an empty guide even though the catalog packet contains the recipe. Resolve the packet through `scoredRecipe.catalog.ref` and use its lines and steps.

### Issue 2
apps/ios/Platform/Persistence/Services/MealLogService.swift:96-99
When a catalog recipe has no same-title legacy recipe, `resolvePersistedRecipeID` inserts only a `recipes` header. It creates none of the `recipe_ingredients` rows used by inventory deductions and journal totals. Logging that meal therefore records zero nutrition and deducts no ingredients. The frozen catalog nutrition reader is not used by production reports. Add a catalog-aware path for logging, reports, and deductions.

### Issue 3
apps/ios/Feature/CookMode/CookModeViewModel.swift:302-305
Resuming or adjusting a timer passes `label: nil`, so this branch drops the notification update. Pausing already cancelled the notification, which means a resumed timer has no alert. Adjusting a running timer leaves its alert at the old time. Look up the stored timer label when applying the scheduling effect.

### Issue 4
apps/ios/FeatureLogic/CookMode/Plan/PlanAllocating.swift:477-483
`allocateDinner` reads the unchanged `pool` for every line and reserves stock only after choosing the whole dinner. A swap can therefore consume an ingredient that another line still counts as available. For example, a 100 g shortfall can claim 100 g of substitute stock, then the substitute’s own recipe line can claim those same grams. Both appear sufficient and the shopping list misses the shortage. Reserve each allocation in a local working pool before checking the next line.

### Issue 5
apps/ios/Platform/Persistence/KitchenPlan/PlanDataAdapter.swift:280-282
A swap-covered line is saved under the original ingredient with its full requirement and no shortfall. `loadReservedGrams` ignores the swap and reserves that full amount against the original. It leaves the substitute stock available for another plan, even though the saved dinner needs it. Persist the actual original and substitute amounts consumed, then restore reservations from those amounts.

### Issue 6
apps/ios/Platform/Persistence/Repository/RecipeRepositoryCatalogRecommend.swift:87-94
Legacy search requires the `.lowCarb` tag for a keto profile, but this catalog gate checks only vegetarian and vegan claims. A keto user with ingredients for a high-carb catalog recipe can therefore receive it alongside results that passed the existing diet filter. Apply an equivalent low-carb check, or exclude catalog recipes whose compatibility cannot be established.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This integration joins the recipe catalog, taste learning, LeChef tools, community service, and supporting checks.

  • Catalog recipes join pantry matches and cooking flows.
  • Onboarding adds only ingredients found in kitchen photos.
  • The planner builds dinner plans from available kitchen stock.
  • Cook Mode guides recipes with saved steps and timers.
  • Live cooking suggestions wait for approval on the phone.
  • Cooking and recipe choices shape a local taste profile.
  • Community features connect through typed app and server paths.
  • Reverse scan previews and logs the chosen recipe consistently.

Acknowledged limitations:

  • obvious-autobuild[bot] records the residual taste ranker as intentionally off by default.
  • obvious-autobuild[bot] records the late-rating correction path as intentional.
  • obvious-autobuild[bot] records reverse-meal-eval as unrun.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Catalog[Catalog recipe] --> Match[Catalog recommendation]
  Match --> Preview[Preview reads catalog packet]
  Match --> Guide[Guide expects legacy recipe ID]
  Guide --> Empty[Catalog-only guide has no steps]
  Match --> Log[Meal logger resolves integer ID]
  Log --> Header[Creates recipe header only]
  Header --> Reports[Reports and deductions read legacy ingredients]
  Reports --> Zero[No ingredient rows means zero totals]
Loading

Reviews (1) · Last reviewed commit: "fix(ios): expect the reviewed egg-tofu s..." · Reviewed by Greptile

Comment thread apps/ios/Feature/Recipe/CookingGuideView.swift
Comment thread apps/ios/Platform/Persistence/Services/MealLogService.swift
Comment thread apps/ios/Feature/CookMode/CookModeViewModel.swift
Comment thread apps/ios/FeatureLogic/CookMode/Plan/PlanAllocating.swift
Comment thread apps/ios/Platform/Persistence/KitchenPlan/PlanDataAdapter.swift
@SamGu-NRX
SamGu-NRX changed the base branch from main to alpha October 16, 2026 04:28
@SamGu-NRX
SamGu-NRX merged commit 485ae82 into alpha Oct 16, 2026
4 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.

3 participants