Skip to content

feat(release): the release-candidate build with every late fix, the goal path and the safety sweep green - #188

Merged
SamGu-NRX merged 98 commits into
alphafrom
obv/burst9-fridgeluck-rejoin
Oct 16, 2026
Merged

SamGu-NRX merged 98 commits into
alphafrom
obv/burst9-fridgeluck-rejoin

Conversation

@obvious-autobuild

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

Copy link
Copy Markdown

The release-candidate build with every late fix, the goal path and the safety sweep green

obv/burst9-fridgeluck-rejoin — the join branch for wave 9 and every wave 5–8 branch not contained in the previous join. Base: obv/burst9-fridgeluck-safety-gates (not main).

Human author: Sam Gu (sgu07966@gmail.com). Work performed by the Obvious Autobuild agent on Sam's behalf.

What changed and why

The previous join (obv/burst9-fridgeluck-safety-gates) contained the safety-gate wave. Three wave-9 branches and seven older branch heads were still outside it, so the release candidate could not be cut. This branch merges, in order:

Branch Head merged PR / content
obv/burst9-fridgeluck-screen-path 25f23ad #185 — serve catalog recipes to the assistant, finalization, and cloud ranking
obv/burst9-fridgeluck-lechef-socket 8755e92 #186 — real-WebSocket cook-session E2E; live session gateway
obv/burst9-fridgeluck-taste-gate 9096d45 #187 — recipe bundle safety classes on every generated recipe
obv/burst8-fridgeluck-join 34d74e1 burst8 join (plan overhaul integration)
obv/burst8-fridgeluck-goal-path 13d9a7a goal path — fridge→meal→taste
obv/burst6-fridgeluck-trunk 54ce8ca burst6 trunk
obv/burst6-fridgeluck-catalog-cook-flow d6252a7 #174 — catalog cook-to-log with frozen nutrition
obv/burst6-fridgeluck-community-client-groups f7fb825 #179 — two-user live-server community walkthrough
obv/burst6-fridgeluck-taste-live c737eb8 taste live updates
obv/burst8-fridgeluck-catalog-reach 6b8a348 catalog reach into recommendations

Post-merge repairs, each its own commit:

  1. Mirror re-pins (chore(tools)): the trunk merge changed TasteEventStore.swift, TasteCatalogFeatureIndex.swift, and the shared TasteEventStoreTests slice; refresh.sh refused to overwrite stale pins, so SOURCES.txt/TESTS.txt were re-pinned to the merged sources and TasteStoreLinuxTests.swift regenerated (the regenerated text matched the committed mirror — no text drift, only stale pins).
  2. Bound-nonce test repair (test(tools)): testTransparentRefreshRefreshesTheAccountSnapshot called SessionViewModel.signIn without the appleRequestNonce() preamble the merged sign-in flow requires (SPEC 3.3 nonce binding); the call silently no-oped as .signedOut. One line added; the test then exercises what it intended.
  3. Apple-toolchain access control (fix(ios)): SubstitutionService.loadBridge(db:DatabaseQueue) / loadBridge(db:Database) returned the file-private Bridged struct at internal visibility — rejected by Apple toolchains, tolerated by Linux. Both are now private; the only caller is internal.

The wave-9 PR descriptions asked for "one-line wirings in AppDependencies.swift" only from the screen-path PR (#185); its body is CI-status-only (verified via the issues API, no edit history), and every construction seam it needs — makeTasteProfileViewModel(), makePlanSheetViewModel(), makeRecommendationEngine(), the community view models — is already in AppDependencies.swift from #182 and the wave's own commits. No additional wiring was required.

How this was checked

All on Linux x86-64 Swift 6.4 (swiftly) + Bun, unless marked hosted:

  • Data regeneration: python3 data/recipes/tools/compile_bundle.py → 762 recipes, 328 ingredients; output byte-identical to apps/ios/Resources/RecipeCatalog/recipes-v2.json (determinism held through the merge).
  • Validators: validate_bundle.py PASS (fail-closed null claims only, by design); docs/spec/recipes/contracts/check.py PASS (50 identity entries, 6 recipes, 6 events); pytest data/recipes/tools/tests/ — 38 passed, 1 skipped (pre-existing skip).
  • taste-store-check: mirror in sync; test.sh — 53 tests, 0 failures.
  • community-check: refresh.sh re-pinned 33 sources; verify.sh PASS; swift test — 82 tests, 0 failures after the nonce fix above.
  • substitution-check: OK — 70 swaps, 126 mapped endpoints, 329 composition keys; sweep allowed=597 askUser=260 refused=53.
  • Backend suites: backend/community bun test — 138 tests, 0 fail (26 skip, pre-existing); backend/gemini-agent bun test — 338 tests, 0 fail.
  • LeChef E2E (apps/ios/Tools/lechef-e2e run.sh): 12/12 boot checks — real WebSocket, production phone-side walk; includes the substitution recheck receipt carrying the contract-shaped allergen recheck ({"verdict":"verified","eaters":["Ana"],"blockedPath":"Contains wheat via soy sauce"}), which verifies the receipt-shape divergence PR [obvious-fleet] real-WebSocket cook-session E2E for phone and gateway, goal-path green #186 documented as open.
  • Community E2E (scripts/community-e2e/run.sh): WALK PASS — 13 steps — nonce-bound 18+ sign-in, publish, Explore, Tables, report→queue→decision, challenge entry, 24 h vote gate, closed tally, restore.
  • Hosted macOS CI ("iOS CI"): dispatched per head through ten rounds to green. Early rounds surfaced merge artifacts the Linux mirrors cannot catch: run 38848376912 failed at 268b38b (access control on the private Bridged return), 38848749128 at 46609b4 (stale snapshot identifier), 38849749630 and 38850780087 (adapter merge artifacts), 38852735186 (stale merge base — resolved by merging the advancing safety-gates tip), 38854677426 at 58eba2d (missing bridged argument), 38855840833 at bc155f2 (runtime; the base's sweep fixes and goal-path green lineage landed in bd8af1b and were merged), 38857675958 at 917a550 (one stale transparent-refresh test missing the nonce-bound sign-in setup), 38859000781 at 78380ee (compile: an unbridged-substitute guard returned false where the verdict type requires .allowed, fixed in 9ca20cd). Run 38860269509 at 9ca20cd: SUCCESS — build-and-test and dead-code-advisory pass; the goal path and the safety sweep run green on the merged tree. Those workflows belong to the default branch's .github; this staging lineage carries only ios-ci.yml, matching the coverage every prior wave PR received.

Unresolved review findings, PRs #166–#180

PR Reviewer Finding Status in this tree
#166 Greptile 8 findings (round 1) All addressed in round 2 at 015fc30, each with a regression test (per the PR's own comment thread).
#172 Codex 3 changes-requested Fixed at 5a33152 (documented in the PR body dispositions; CI-verified there).
#173 Codex/independent 5 findings incl. stale canVoteAt after refresh Fixed: refreshAccessToken now returns the fresh account and updates the snapshot (unit-tested; community-check 82/82; community E2E step 10–11 exercises the gate live).
#178 Greptile Swaps bypass blocked allergens (P1) Fixed on the cook path by #180's union screening (parsedAllergenIds ∪ dietaryExcludedIngredientIds) and the shared safety gate; cook-time recheck blocks fail-closed incl. unknown composition (CookSubstitutionRecheck, e2e S4).
#178 Greptile Swaps double-count pantry stock (P1) Open — documented follow-up (plan-side stock accounting).
#178 Greptile Leftovers pool portions across dishes (P1) Open — documented follow-up.
#178 Greptile Saved catalog IDs become zero (P2) Open — plan-sheet persistence path; not touched by this join.
#178 Greptile Catalog overrides explicit staple condition (P2) Open — documented follow-up.
#180 Codex Bridge snapshotted at init (P1) Fixed in 1cf8a83a (per-query read; regression shipped).
#180 Codex Health-profile read failure not fail-closed (P2) Fixed in 1317e365 (read failure → blocked verdict; regression shipped).
#180 Codex Explicit allergen IDs not screened (P1) Fixed in 1317e365 (union screening; regression shipped).
#184 Greptile Assumed staples not added in app mode (P1) Moot while the variant ships disabled — no tested arm passed the held-out app-pool gate and the learned default stays off (results/ + gate note). Revisit if an arm passes a future gate.
#184 Greptile Chained scores escape the band (P2) Fixed in this tree — _churn_bounded_reorder splits into maximal runs and never swaps across runs, so wide gaps survive regardless of chaining (evaluation/taste-lab/taste_lab/engine.py:437).

PRs #169, #170, #176, #177, #181–#187 had no unresolved bot findings on their threads; #181's and #186's bodies document their own final verification.

What was not checked

  • Nothing was built or run on Apple hardware — iOS app tests run only on the repo's hosted macOS CI; no device, no Xcode.
  • The hosted macOS build takes the branch's word for UI behavior: no screenshots, no interaction runs.
  • dead-code-advisory runs on CI only.
  • The community E2E's run.sh is referenced by the package header but lives outside apps/ios/Tools/community-e2e/; it is present at scripts/community-e2e/run.sh and was used as committed.

Assumptions

Merge

Base is alpha — the integration branch the late wave PRs target (retargeted during the round-nine union) — and not the repository default; per the repo's merge policy this PR is left as a draft for human review and human merge.

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

ObviousApp and others added 30 commits October 16, 2026 01:30
…ort from the base branch

Hosted build-and-test failed at the PR head with compile errors in
ReverseScanService.swift and ReverseScanMealView.swift (ScoredRecipeID ?? Int64),
inherited from the base branch at the original branch point; the base branch
fixed exactly this in 832c592 and 3826378. Merging the base head resolves
the inherited breakage; those commits touch only apps/ios sources and tests.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
… on nil recipe identity

Review findings 1 and 3 on PR #170 (comment 6205336377):

- The trunk integration test ingested the same store-loaded array into both
  engines, so its parity assertion held by construction. The direct baseline is
  now rebuilt from the meal log's writer shapes (three 5-star cooked events,
  each anchoring its own cook transaction, plus one rated event linked to the
  last cook); only the store's identity and replay-order columns (id, at) are
  borrowed so the engine's lazy decay sees the same instants on both sides. The
  shared Linux slice gains the same parity test over fully controlled records,
  and a mutation check (kind misread in the store's read path) trips it:
  1.771 vs 0.328 contributions.
- events(recipe:) and latestCookEventId(for:in:) bound recipe.source into
  recipe_source = ?, so a contract ref with nil source silently matched
  nothing. Both query entry points now throw
  TasteStoreError.unresolvableRecipeIdentity; fail-first tests recorded two
  failures before the guard landed.

Local: taste-store-check 52/52 after re-pinning (49 before).

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

An exemption entry missing old_kcal or new_kcal previously passed silently:
abs(entry.get(field, nan) - value) is always False for NaN, so the
must-reproduce-both-values contract held only when the fields existed.
Report the missing field explicitly instead. The new regression test fails
before this fix (planted field-less exemption produced zero errors) and
passes after; full data/recipes/tools suite is 18 passed with the one
pre-existing environmental byte-identity failure unchanged.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The claims schema has no low-carb field, so no catalog recipe can prove
keto compliance, while the legacy pool enforces the .lowCarb tag mask —
without this arm a keto user loses every legacy result yet keeps every
catalog result. Fails-first: testKetoDietExcludesCatalogPoolFailingClosed
reproduces the ungated catalog entry before the gate arm lands.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Catalog entries materialize as nil-id legacy stand-ins, so Start Cooking
rendered an empty guide, loadIngredients returned nothing, and Le Chef
received zero ingredient context. Add catalogCookPacket(for:) and
cookPacketIngredients(_:) on the repository — the single structured path
— and route the preview drawer, the cooking guide, and the assistant
context through it. Fails-first: the packet path had no repository API
before this commit and
testCatalogRecommendationYieldsCookPacketAndBridgedIngredients pins
non-empty steps and bridged ingredient lines for a catalog result.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
TasteEventStoreTests declared cookedEvent -> TasteEvent while
constructing a TasteEventRecord, which Linux Swift 6.4 rejects with 16
conversion errors (reproduced in the portable harness; fails-first).
Ports trunk commit 9de2985 so the Linux portable checks compile on this
lineage too. macOS CI compiles both forms; semantics are unchanged.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Two review findings, both in glue no test exercised: the guide resolved
the cook packet only through a legacy integer id, so catalog-native
recommend results (Recipe(id: nil, ...)) fell back to the legacy path
with a synthetic ref, and the journal computed macros from legacy
ingredient rows instead of the frozen snapshot the meal was logged
against. The gate is extracted from CookingGuideView into a static
helper so tests drive the exact production resolution; the journal test
logs a real catalog meal through the full flow and asserts the journal
reports the snapshot. Both new tests fail at this head — the helper and
journal still resolve the old way; the next commit fixes them.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The iOS-only helper above the Linux-mirrored slice was declared -> TasteEvent
while constructing a TasteEventRecord, so the macOS test build failed with seven
type-mismatch errors the mirror cannot see (the slice region regenerates its own
helper from the committed preamble). Match the preamble's return type.

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

Review round on PR #178 (five findings), each pinned by a failing-first
regression test in LeChefPlanWeeklyTests:

- Blocked substitutes are never offered or applied: the snapshot drops
  swaps whose substitute is a household allergen, and the allocator
  refuses any swap whose substitute an eater blocks (previously only
  recipe lines were screened).
- Stock is claimed within a dinner: a line's swap sees the pool minus
  what earlier lines of the same dinner already claimed, so the
  substitute line reports its real remainder instead of hiding it.
- Leftover surplus is tracked per cooked dinner: a leftovers night
  re-serves one dish and must be covered by that dish's own surplus,
  never by portions pooled across different dishes.
- Saved catalog dinners reload their numeric identity (the negative
  digit tail of the rcp- id) instead of 0, keeping replacement and
  exclusion working after a plan reload.
- Catalog assumed_staple defaults yield to an explicit household
  pantry tier: an ingredient marked only_if_confirmed is never
  re-assumed.

Verified: Linux scratch harness 48/48 (the six new/updated tests fail
before the fixes and pass after). Hosted macOS CI runs the app-target
suite.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The fail-first run proved both review findings (38835855949 red at
5c8ac46): the catalog-native gate test got nil content, and the journal
reported 0.0 kcal where the frozen snapshot says 840.

The guide now resolves catalog content from the result's own catalog ref
when there is no persisted integer id — the shape the recommend pool
builds for every catalog-native recipe — keeping the legacy bridge and
the parsed-instructions fallback for drafts and unloadable packets. The
journal reports the frozen per-serving nutrition snapshot at the linked
revision, scaled by servings consumed and portion multiplier, and keeps
the legacy ingredient math for cooks with no catalog link. The bridge
test inserts the legacy recipe before referencing it (FK order).

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

Review fixes (comment 6205308578):

Finding 1: apply logged the exposure from the input pool before
reordering, so once the learned layer is on the logged slate was not
the order the user saw — a promoted recipe never accrued skip/view
evidence against a slate it appears on and slatePosition indexed the
wrong entries (SPEC 2.4 pins the exposure as the shown slate,
ordered). Reorder first, then log; the current render's exposure also
stays out of its own ranking inputs. The exposure test now asserts
the displayed (learned) order and fails against the old code.

Finding 2: the taste-store-check mirror failed to build — the
mirrored TasteProfileRepository referenced TasteCatalogFeatureIndex,
which the mirror does not carry (reproduced at c149e08 on Linux
Swift 6.4.1: cannot find type 'TasteCatalogFeatureIndex' in scope).
Declare a TasteCatalogFeatureIndexing seam next to its only
consumer; the concrete index conforms in the app target and the
mirror compiles the repository against the protocol alone. The
portable suite passes 50/50 after the change. test.sh gains a Swift
6.4.1 fallback path: GRDB 7.10 needs tools >= 6.1, so the stale 6.0.3
entry can no longer resolve the graph.

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

The committed golden fixtures were regenerated over recipes-v2.json,
so the README's validate/regenerate commands still passing the legacy
data.json dead-ended with golden-hh23 ranking mismatches. Verified
failing-first with the legacy path and replaying exactly with the v2
path.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The finding-1 assertion flip was left uncommitted by 04ffc3c, so CI ran
the new reorder-then-log service against the old expectation and failed
with the inversion it was written to catch: the logged slate is the
learned ["3", "2", "1"], not the rule order ["3", "1", "2"].

Co-authored-by: Sam Gu <sgu07966@gmail.com>
RecipeCatalogNutrition lives in the FLFeatureLogic package module;
RecipeRepositoryCatalog already imports it, UserDataRepository did not.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
…s and transport failures

- Leave keeps the local Table until the server accepts; an owner's leave surfaces ownerCannotLeave before any request is sent (the backend refuses it with 409).
- Join and createTable transport failures enqueue outbox events so intent survives; a pending join restores as asking-to-join after relaunch (community_table_link_v2).
- Restore keeps tokens when the refresh leg could not run and clears them only on a server rejection; refresh persists the fresh account snapshot it carries.
- The in-memory token store serializes async access with NSLock.withLock (Apple SDK forbids lock/unlock across await).
- Failed no-content responses keep their bodies so typed server errors surface.

Review round for PR #173 findings 1-7, each with a failing-first regression test.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
- CommunityStore.swift:464: Data(row["payload_json"]) has no exact initializer match on the Apple SDK (resolved only on Linux); read the column typed per the file's existing pattern and treat a NULL payload as no cache.
- CommunitySessionService.swift: with try? optional flattening (SE-0230) the paired tokens != nil comparisons were always true (compiler warnings at lines 97 and 107); compare the try? result directly.

macOS CI run 38838725369 failed on the initializer; this fixes it.

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

The packet PRs shipped the community client stack (#159/#166/#173/#179),
the taste-profile repository (#165/#167/#176), and the LeChef plan sheet
(#152/#178) with no app-side construction site. AppDependencies now builds
them at launch: CommunityStore + CommunitySessionService (keychain token
store) + the three community view models, and TasteProfileRepository over
the same shipped-bundle feature index the residual ranker uses. The
profile and plan-sheet view models are exposed through factories backed
by the production adapters. The community backend URL resolves from
COMMUNITY_BACKEND_BASE_URL (env or Info.plist), defaulting to the local
dev server on port 8087.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
catalogMatches ranked every installed catalog row regardless of rights
status. Filter to rights_status = 'original' so a rewritten recipe can
never surface in Home's pool, matching the gate report's requirement
that only originals are recommendable.

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

One end-to-end walk over a fresh on-disk database, shipped resources, and
the real AppDependencies: scan-review pantry intake with a household
allergen, Home's exact recommendation source under the off residual gate,
a catalog cook on the guide's session construction with the structured
timer and thermometer steps, a recorded timer_start proposal approved on
the cooking session, the celebration-path meal log with the frozen
(catalog_id, revision) nutrition snapshot and pantry consumption, the
taste profile's cook evidence below the render gate, a weekly plan
through the production adapter with allergen screening, and a signed-out
share waiting in the community outbox.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
PlanSheetViewModel lives in the FLFeatureLogic framework target; the new
factory needs the module import to compile.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The transport stub's async send() called NSLock.lock()/unlock() directly, which the Apple SDK forbids from async contexts (macOS CI run 38839520976). Wrap the send body in NSLock.withLock; the synchronous sentCount/at accessors keep their existing locking.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
LiveProposalFrames.proposal takes Data, not String; MealLogService.Outcome
spells historyId with a lowercase d; and GRDB's sync read overload must be
chosen from a sync context, so the async test reads through a synchronous
helper shell.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The previous round's mechanical edits were lost to a concurrent write; this
commit re-applies them on top of the helper additions.

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

Of 215 vocabulary keys carrying only 'unknown', 210 now carry decisive
composition classes with a recorded class_source: 47 from the suite
allergen table, decisive USDA FoodData Central catalog categories for a
further set, and per-key curation where catalog categories were
unreliable (steak listed as vegetables, cinnamon as baked products).
Ambiguous cases keep the 'unknown' fail-closed marker; five keys remain
fully unknown (broth, dashi, green_curry_paste, massaman_curry_paste,
stock). Mustard gains its allergen class.

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

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

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

The bundle now carries what the app's diet and safety gates need instead
of coarse proxies:

- ingredients ship their vocabulary composition classes; the schema
  requires them.
- thermometer checks cite their FSIS class and the validator checks the
  cited row's endpoint minimum instead of a coarse class floor, which
  falsely rejected valid ground-meat and leftovers checks.
- authored timer_min metadata wins during compilation; text parsing
  remains the fallback (940 authored steps ship their exact seconds).
- the compiler auto-places a charted FSIS check when a recipe's
  required classes are not covered by any authored safety block (none
  needed on the committed corpus).
- vegetarian, vegan, gluten_free, and dairy_free claims are derived
  from line classes fail-closed: an authored true claim contradicted by
  a known blocking class fails validation; unknowns keep the authored
  value and warn.

Recompiled bundle: 762 recipes, 328 ingredients, 468 thermometer checks,
1170 timered steps, derived true claims vegetarian 327 / vegan 121 /
gluten_free 336 / dairy_free 306. Validator and contract checker pass;
portable suite 37 passed, 1 skipped.

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

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

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
reach_report_v2.py resolves ten written household pantries (pantries-v2.json)
against the app ingredient space by exact match only (snake_case and
plural-fold candidates; every resolution rule recorded, misses disclosed)
and counts, per pantry, recipes cookable with 0 and at most 2 missing
required lines, split by the app's fail-closed diet gate.

Shipped bundle vs this branch's build, medians across pantries
(near tier, at most 2 missing lines): omnivore 129.5 -> 129.5,
vegetarian 3 -> 83.5, vegan 0 -> 23.5, gluten_free 5.5 -> 60,
dairy_free 3 -> 59.5. Identity reach is unchanged; the diet-gated reach
gain comes from the composition classes carrying the claims.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
GRDB's sync read is a plain throwing call, so a rethrows wrapper may not
forward to it.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
The test calls the @mainactor signInAdult helper, sending the non-Sendable test case across the actor boundary (Swift 6 sendability error at CommunityClientTests.swift:513, macOS CI run 38840329632). Every other test in the class already carries the annotation.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Two goal-path assertions described behavior the stack does not implement:

- Pantry deduction: applyConsumption reads legacy recipe_ingredients by
  the resolved recipe id; a catalog pool entry materializes an
  ingredient-less legacy row, so a catalog cook deducts no stock. That
  is the inventory-deduction gap tracked as a feature-sized follow-up —
  pin the empty consumption result and unchanged total instead of
  asserting a drop.
- Learned-layer evidence: the hidden layer's numerator counts cooks
  rated 2 stars or below, so a 4-star cook contributes denominator only.
  Assert the held low-rating claim by denominator, not numerator.
The sweep test was written without a compilable host: the recommendation
surfaces take Set<Int64> pantries, HealthProfile.dietaryRestrictions is a
JSON array string rather than an array, and database reads/writes inside
async test bodies resolve to GRDB's async overloads and need await; the
unknown-class mutation check hoists its fetch out of XCTUnwrap's
autoclosure, which cannot carry async calls.
ObviousApp and others added 8 commits October 16, 2026 05:01
loadSnapshot's read closure drives substitutions(for:in: db), whose
household context and offer screening still re-entered the database
queue (householdScreen's profile and vocabulary reads, verdict-time
bridge lookups) — a held-transaction re-entry GRDB aborts with
"Database methods are not reentrant". Add handle-based householdScreen
and bridged screening variants that run every read on the caller's
connection, and complete the persistence fixture's catalog-ingredient
schema with the classes column the snapshot read now selects.
The screenedSubstitutions path still called substituteScreenVerdict
without the bridged parameter; queue-safe callers read the bridge per
query, so bridge() is correct here.
obvious-autobuild Bot added a commit that referenced this pull request Oct 16, 2026
…n land green

Merge ab6c9f1 brought the safety-gates feature whose sweep suite has never run green anywhere: CI run 38856495680 fails 934 sweep assertions plus one substitution-screen case, and the rejoin line's repaired head bc155f2 (run 38855840833) fails the same signature — the breakage is base-borne on obv/burst9-fridgeluck-safety-gates, not introduced by this branch's merge. PRs #187/#188 own that repair; this branch is the LeChef socket E2E work.

Reverting merge ab6c9f1 restores the aea3b8f state (alpha + goal-path tip merged), where GoalPathTests.testFridgeToMealToTasteGoalPath is green — the original blocker of this PR, fixed by carrying e87ee7b's pin of actual pantry/evidence behavior.

Co-authored-by: Sam Gu <sgu07966@gmail.com>
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
ObviousApp and others added 6 commits October 16, 2026 05:44
The sweep drove PlanDataAdapter.loadSnapshot — which reads the household
from the health_profile table — without ever seeding that table, so the
weekly-plan pool screened unrestricted while the test-side re-screen
used the restricted profile: blocked recipes were offered and keto
candidates leaked. Seed the swept profile before loadSnapshot, screen
rcp-* pool rows through the composition class screen and legacy rows
(numeric ids from the weekly-plan merge) through the documented
required-flagged-line filter, and guard the unrestricted pool reach
instead of an exact-tier count the FDC bridge coverage cannot support
in a fresh-install database.

Restore the base's canonical substituteIsAllergen: the merge resolution
had rewritten it into a composition recheck whose empty-bridge nil broke
the explicit-id/no-diet contract (an unflagged substitute must stay
allowed when no diet is active). The composition screen stays on the
offering surfaces; the id/diet gate keeps the base's semantics.

Flip the dropped-group-mapping mutation check to its achievable
contract: the gate's group-to-class fallback keeps refusing a dropped
mapping, so the mutation asserts both refusals survive.

Also merge base tip e87ee7b (test-expectation alignment for the
shipped bundles).
…es' into obv/burst9-fridgeluck-rejoin

# Conflicts:
#	apps/ios/Platform/Persistence/Services/SubstitutionService.swift
…to obv/burst9-fridgeluck-rejoin

# Conflicts:
#	apps/ios/Tests/LeChefPlanWeeklyTests.swift
Three surfaces still disagreed about the one household safety screen:
catalogMatches computed the household and composition data but offered
recipes without applying the verdict, so restricted households received
recipes the sweep's own screen refuses. Apply screenCatalogRecipe before
the claim gate, exactly as the weekly-plan and substitution surfaces do.

Substitute offers without a bridge mapping returned nil (treated as
blocked) even for unrestricted households, which have nothing to break;
return allowed for them and keep fail-closed nil for restricted ones.

The sweep fixture applied only the v2 bundle, so the identity bridge
resolved nothing: it matches through `ingredients`, which was empty.
Hydrate the shipped USDA vocabulary first, then the v2 catalog whose
apply rebuilds the bridge against it, mirroring the app's warm-up order.

The transparent-refresh test signed in without building the Apple nonce
first, so SessionViewModel.signIn exited before contacting the service;
build it like the canonical helper does.

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

The merged bound-nonce sign-in flow refuses a token minted without a nonce
request; the stale transparent-refresh test skipped appleRequestNonce() and
its sign-in was refused, leaving the account snapshot nil. Matches the
sign-inAdult helper and the passing community-check mirror.
…to obv/burst9-fridgeluck-rejoin

# Conflicts:
#	apps/ios/Tests/Community/CommunityClientTests.swift
…itutes

ee38b90 differentiated the no-bridge-mapping guard but returned false
where the verdict type needs .allowed; the caller unwraps isAllowed, so
unrestricted households keep the substitute either way — as Bool the file
no longer compiles.
@SamGu-NRX
SamGu-NRX marked this pull request as ready for review October 16, 2026 06:51
@SamGu-NRX
SamGu-NRX merged commit ef236fc into alpha Oct 16, 2026
5 of 7 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T06:54:01.328823Z 9ca20cd Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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.

SamGu-NRX added a commit that referenced this pull request Oct 16, 2026
Alpha (ef236fc, via #188) already carries everything this branch adds:
the lechef-e2e tool, the backend e2e boot/script, the agentServer split,
GoalPathTests with the pantry and evidence pins, and the AppDependencies
wiring are byte-identical on both sides. The one branch-only difference,
the rights_status filter in RecipeRepositoryCatalogRecommend.swift, is
also on alpha alongside the household safety gate.

The automatic merge was wrong: the branch's revert of the safety-gates
merge (43ae74d) made git treat alpha's repaired safety-gate code as
deleted, so it silently dropped HouseholdSafetyGate, the resolver, the
sweep suite and the v2 bundle classes. This merge takes alpha's tree
as-is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

2 participants