Repository navigation
feat(substitutions): canonical-key substitution table over the catalog vocabulary with real allergen rechecks - #177
Draft
obvious-autobuild[bot] wants to merge 2 commits into
Conversation
…able Map PR #114's reviewed substitution pairs onto the base branch's canonical ingredient vocabulary instead of the legacy app ingredient-id space: exact name/alias/fdc-id/USDA-reference evidence only, fail-closed on ambiguous or conflicting matches, self-swaps and duplicate key/role offers dropped, per-key composition map with the compound allergen/diet closure, unknown composition fail-closed to known=false. Regenerated resource: substitutions-v2.json ships 70/279 reviewed pairs covering 126/317 endpoints (the legacy app-id table shipped 35/40) with 50 swaps carrying known substitute composition; 209 pairs drop with a named unmapped-key audit. 32 converter tests; regeneration from the pinned pairs+vocabulary is byte-identical. Co-authored-by: Sam Gu <sgu07966@gmail.com>
…llergen rechecks SubstitutionTable decodes schema-2 canonical-key resources (rejects v1), looks swaps up by canonical original key, and screens the substitute's composition per eater: forbidden classes refuse naming the class, unknown composition fails closed to ask-the-user under any active restriction, unknown restriction ids add no forbids. SubstitutionService loads the v2 resource, translates between app ingredient ids and canonical keys through the catalog identity bridge (legacy id -> canonical keys -> ingredient rows), assembles deterministic offers, and answers the cook-mode recheck from real composition facts — the cooking guide no longer reports unknown for every substitute. substitution-check v2 pins audit counts, canonical-key integrity, recheck/compositions agreement, the all-pairs x all-restrictions sweep, and the reviewer compound-closure examples (yogurt carries dairy, tofu carries soy); 13 XCTest cases cover table semantics on an inline miniature document. Co-authored-by: Sam Gu <sgu07966@gmail.com>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bridges PR #114's reviewed substitution table onto the base branch's canonical ingredient vocabulary (329 entries) instead of the legacy app ingredient-id space, and re-arms the cook-mode allergen recheck with real composition facts.
Why
The shipped substitution resource indexes pairs by legacy app ingredient IDs and offers only 35 of 279 reviewed pairs. The reviewed table no longer depends on the app's id space; the catalog's canonical keys are the stable join. The legacy wiring also made cook mode report every substitute as unknown composition (
{ _ in nil }), so proposal-driven swaps always failed closed — correctly, but uselessly.What changed
scripts/substitutions/convert.pymaps pair endpoints to canonical keys via exact evidence only (canonical names, recorded aliases, fdc-id equality, recorded USDA source names), failing closed on ambiguous/conflicting matches. Self-swaps created by canonicalization and duplicate (original, substitute, role) offers are dropped (first pairId wins). Emits a per-key composition map with the compound allergen/diet closure (vocabulary curated classes ∪ mapped facts store); keys nothing covers fail closed tocompositionKnown: falsewith empty class lists. Regeneration is deterministic (byte-identical from the pinned inputs).substitutions-v2.jsonships 70/279 reviewed pairs covering 126/317 endpoints (legacy: 35 pairs / 40 ingredients), 50 swaps with known substitute composition, 209 dropped pairs recorded with named unmapped-key audit.SubstitutionTable(Swift) decodes schema 2 canonical-key resources and rejects non-v2; lookup by canonical original key; screening refuses naming the forbidden class, fails unknown composition to ask-the-user under any active restriction, and unknown restriction ids add no forbids.SubstitutionServiceloads v2, translates app ingredient ids ↔ canonical keys through the catalog identity bridge (legacy id → canonical keys → ingredient rows, min id for determinism), assembles deterministic offers, and answers the cook-modesubstituteIsAllergenrecheck from real composition facts.CookingGuideViewis wired to it.substitution-checktool + XCTest cover the semantics; README documents the v2 regen flow.How I checked
python3 -m pytest scripts/substitutions/ -q→ 32 passed (canonical evidence, ambiguity/conflict fail-closed, self-swap/dup rejection, composition merge + screening, CLI, generated resource).cmpbyte-identical.bash apps/ios/Tools/substitution-check/run.sh(Linux swiftc, compiles the production table + checker against the real resource) → OK — 70 swaps, 126 mapped endpoints, 329 composition keys, sweep allowed=597 askUser=260 refused=53. Includes: no self-swaps/dups, composition coverage for every endpoint, recheck↔compositions agreement, vegan refusal names dairy (yogurt), soy-free refusal + gluten-free allowance (tofu), egg→banana resolves, schema gate rejects v1, determinism.swiftc -parseclean on all five changed Swift files.Not checked here
SubstitutionService.swift,CookingGuideView.swift, andSubstitutionTableV2Tests.swiftwere syntax-checked (-parse) but not type-checked or run — onlySubstitutionTable.swiftwas fully compiled (with the checker) on Linux. macOS CI is the real gate for the app target and the XCTest target.ReverseScanService.swift/ReverseScanMealView.swifterrors on its head) when this branch was cut from4ba2043; this PR's diff doesn't touch those files.Assumptions
substituteKeyrides along in the legacySubstitutionmodel for display.buttersubstitute; the reviewer dairy example usesyogurt(6 shipped swaps), which carries the same compound closure.Substitution.substituteIdsemantics preserved: the resolved app row id, not a canonical key string.Human author: Sam Gu (sgu07966@gmail.com)
🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact
CI on head 63f8301
build-and-testrun 38816535943: fail — inherited from the base stack. The only error isRecipeCatalogRecommendTests.swift:179:7: cannot find 'CookThermometer' in scope, the pre-existing failure on base PR [obvious-fleet] feat(ios): recommend from the 763-recipe catalog through the real engine #172 (its current head fails the same check). No error names any file this PR changes: the log showsSubstitutionTable.swiftcompiling inFLFeatureLogic, andSubstitutionService.swift,CookingGuideView.swift, and thesubstitutions-v2.jsonbundle copy all completing in theFridgeLuckapp target. Because the test-target batch aborts on the base error,SubstitutionTableV2Tests.swiftwas in the compile job but its full macOS type-check is not confirmed by this run; the Linux checker's full compile of the table and 13 XCTest cases remain the portable evidence.dead-code-advisory: skipped (upstream job gate).CodeRabbit: pass (bot not eligible).Packet ref: this work is also published as the fleet packet
obv-pr/burst6-fridgeluck-subs-canonical(code branchobv/burst6-fridgeluck-subs-canonical, same head63f8301). If the platform opens a PR from that packet, prefer it and close this one.