Repository navigation
[obvious-fleet] join: integrate the overhaul slices (burst5/6/8) into one branch from main - #181
Conversation
…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>
…seam' into obv/burst5-fridgeluck-rcp-loader
…obv/burst5-fridgeluck-rcp-pipeline
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>
|
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 |
… 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>
… obv/burst8-fridgeluck-join
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>
|
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. |
[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.
|
Human author: Sam Gu (sgu07966@gmail.com)
What changed and why
This branch is the integration join: one branch cut from
mainthat 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 amerge: 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.swifthad 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— thetaste-store-checkLinux mirror now pins the catalog feature index and its pureFeatureLogicinputs via a mirror-local bundle-URL shim, so the suite runs on Linux without pulling non-portableAppDatabasedependencies.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: therecipe_ratedmerge into an unrated cook now adds the rating as its owncook_ratingevidence at the merge instant (instead of replacing the no-rating update), saves use reward 0.75 and skips 0.15 (newREWARDtable matching the contracts checker), the per-recipelearnedContributionreports 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|mdregenerated 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-beforeis 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]), matchingcheck.py'sHALF_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:137eed07landed the ingest half (the rated-cook merge adds the rating as its owncook_ratingevidence 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), and65928425reconciled it in a rebase and ported the remaining rank/profile half —rank()reports the clamped learned sum with pins counted aslearnedContribution(the same numbertotalScoreadds), 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).testLateRatingMergesIntoCookWindownow 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 thetaste-store-checkmirror 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 killedLeChefPlanPersistenceTestsrunners:PlanDataAdapter.loadSnapshot()ran the reviewed-substitution loop inside its owndbQueue.readclosure whileSubstitutionService.substitutions(for:)opened a second read on the same serialized queue (bridge()→loadBridge(db: DatabaseQueue)). The service now has asubstitutions(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, andofferableSwapstakes 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. UndermergedResults' 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"becauseRecipe.idisInt64?). 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 head8dc9329f; no file overlap, merge is clean.8dc9329f(Linux, Swift 6.1.2): the temporary replay harness over the productionTasteResidualEngine(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 run38844186301runs on8dc9329f.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.shmirror sync + suite run; shared slice now 15 tests).community-check: 74/74 passed (bunsource pins verified; Swift test linked with-Xlinker --allow-shlib-undefined— theswift::threading::fatallink 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.shover the canonical-key table).Python suites (
pytest): substitutions + everyday tools 64 passed;data/recipes/tools84 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 --noEmitclean;bun test138 pass / 26 skip / 0 fail (afterbun install; without it the Bun type defs break typecheck — environment, not code).backend/gemini-agent:tsc --noEmitclean;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-labpytest 10/10 passed andpython3 -m taste_lab validate-fixturesreports All fixtures replay exactly; community-check verification script clean andswift test74/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, thetaste-store-checkpattern) replays the three golden household fixtures through theTasteRankerTestscomparison loop — ranking order, per-feature contributions,learnedContribution,totalScore, profile and hidden statements — 7/7 tests passed (swift test, incl.testGoldenFixturesReplayExactly);evaluation/taste-labpytest 10/10 passed;docs/spec/taste/contracts/check.py19/19 examples valid;taste-store-check/Scripts/refresh.shmirror in sync at the new engine pin (0b4a34b3…).What I couldn't check
swift testinFLFeatureLogichas a pre-existingno such module CoreGraphicsfailure, which is why the portable mirrors exist.reverse-meal-evalwas not run (documented as unrun in the prior rounds of this join).5a82b961,edd5cc8d) are authored asObvious <obvious@obvious.ai>without theCo-authored-by: Sam Gutrailer — adding it would require rewriting pushed history (force-push), which is not allowed; attribution is recorded here.Assumptions
reach-after.json|mdare generated artifacts that track the merged catalog state, so refreshing them on the join is correct;reach-beforestays 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_appliedevents, which the checker ignores); the golden fixture does not exercise that path..githubis kept identical to the trunk branch (f6f33e2a) so this PR's CI matches the trunk's hosted macOS workflow.The PR targets
mainbecause the branch was cut from themaintip; no earlier PR ever existed forobv/burst8-fridgeluck-join(open or closed), so this is the join's first PR.Round result: CI run
38845246311on34d74e12compiled cleanly (8m36s — past every earlier compile failure) and ran the suite with zero GRDB reentrant fatals — the snapshot reentrancy fix holds. ThreeLeChefPlanWeeklyTeststests then failed withno such table: cook_plan_line: the hand-builtmakeDatabase()fixture never creates the production cook-plan tables, which the now-working snapshot read touches. The parallel thread repaired the fixture standalone asfb0f868b(productionCookSessionMigrations.migratein 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 run38846607091in 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 —-parsedoes not catch missing-argument errors, which is exactly how twobridged:arguments slipped through to run38844186301and were fixed inead103e1/7456f537.Round result: CI run
38846607091onfb0f868bfixed the missing-table failures but exposed one stale expectation:testCatalogPantryFixtureYieldsAFeasibleWeekpinned 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.jsonegg-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 as1c22f76b— updated list plus a positive assertion that the reviewed 2→3 swap actually rides the dinner lines — and run38847597335is 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
38847597335on1c22f76bis 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 head1c22f76b; 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)
SQLite error 1: no such table: cook_plan_line— the catalog adapter tests build their database through the hand-rolledmakeDatabase()fixture, which never created the cook-plan tables that the snapshot load reads for plan reservations (PlanDataAdapterreservations query).CookSessionMigrationsafter the core schema, mirroring what a real app database carries. Verifiedswiftc -parseclean; the round-trip test's explicit migrate call stays and is idempotent.CI round — swap-aware feasibility expectation (1c22f76)
testCatalogPantryFixtureYieldsAFeasibleWeekexpected[2:100, 3:200, 4:200], got[3:300, 4:200].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 againstPlanAllocating.allocateDinnerand the bundle JSON; the test expectation was stale.swiftc -parseclean.CI round — green (1c22f76)
Test Suite 'All tests' passed; zeroDatabase methods are not reentrantfatals; the previously failingtestCatalogPantryFixtureYieldsAFeasibleWeekpassed with the swap-aware expectation.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 commit485ae82e) 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.CookingGuideView.swift:63): confirmed. Catalog results materializeRecipe(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.handleStartCookingpasses the raw result through. A reviewed fix exists on burst9 (commit770ab5eb:makeCatalogContentresolves the packet throughscoredRecipe.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.MealLogService.swift:99): confirmed (journal, deductions, and a dead reader). For a catalog-native cook,resolvePersistedRecipeIDinserts a header-onlyrecipesrow (no same-title legacy row to match), socomputeConsumedMacros(journal) andconsumptionRequests(inventory deductions) — both readingrecipe_ingredients— see zero grams, andcatalogNutrition(forHistoryID:)has no production caller. The frozen-snapshot journal read is fixed in770ab5eb(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 throughapplyConsumption— has no fix on any branch; it is feature-sized and left open.CookModeViewModel.swift:305): confirmed; open on every branch.resumeTimerandadjustTimerpasslabel: nil;performschedules onlyif 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 intoperform. The view model importsUserNotifications, so a fix plus a scheduler-spy test validate on hosted macOS CI only, not the portable Linux harness.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-dinnerclaimedmap 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.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, andloadReservedGramsselects 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 onPlanSwap(optionaloriginalGrams/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.RecipeRepositoryCatalogRecommend.swift:94): confirmed. The diet gate handles vegetarian and vegan only; keto maps to the.lowCarbmask on the legacy pool whileRecipeCatalogClaimshas 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.