Repository navigation
feat(data): integrate recipe bundle v2 — ship compiled catalog, manifest, and end-to-end coverage - #162
Conversation
data/recipes/SCHEMA.md fixes the corpus recipe format (RS v1): required fields, quantity-evidence kinds, FSIS safety checks, allergen-claim evidence, per-recipe rights records with the six SPEC 3.5 permissions, ID namespaces, and the data/recipes/originals/ layout. Shared contract for the originals task (fl-original-recipes-f1) and corpus ingestion; stable once published. Normative implementation: suite C's rs.py/ rights.py/bands.py on eval/scorecard-runner (PR #88). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
M1 of the corpus task: conversion tooling under data/recipes/tools/ plus the 25 audited-keep bundled seed recipes (RIGHTS-AUDIT.md keep verdicts, branch obv/fl-next-recipe-fidelity at 9b827a3) converted to RS v1 at data/recipes/corpus.jsonl with per-recipe rights records in data/recipes/rights_manifest.json. - data/recipes/tools/convert_bundled.py: builds the seeds from apps/ios/Resources/data.json (bundled ids 1-25 only); grams stay source_grams with provenance citing data.json and the audit entry; ingredient names normalized to the suite alias table where truthful; timers from stated durations (upper bound, seconds as fractional minutes); colour/time doneness on animal-protein steps rephrased to thermometer language with FSIS-cited safety checks on the step where doneness is established; claims written only where the suite AllergenScanner recomputes an empty conflicts list AND the claim is true of the real dish (unrecognized-name blocks documented). - data/recipes/tools/validate_corpus.py: validation loop running suite C's rs/rights/bands (eval/scorecard-runner, read-only) over the owned corpus files, plus id-scheme, dedupe (SCHEMA.md section 7), and JSONL hygiene checks. Validation (suite dir: extracted evaluation/suites/recipes): 25/25 recipes lint-pass, 153/153 ingredient lines resolved (100.0%), 0 unchecked claims, 25/25 rights-clear, 0 quarantined; band counts: breakfast 8 lunch 20 dinner 16; time 0-15:14, 16-30:10, 31-45:1; cuisines american 12, asian 4, indian 1, italian 3, mediterranean 2, mexican 3. Missing bands time:46-60 and time:61+ are expected at M1 and await ingested sources. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
ingest_nhs.py parses the 196 NHS Healthier Families pages carrying Recipe JSON-LD, maps quantities to RS v1 evidence using only source-stated grams, suite-table household refs, or honest unknowns, and skips any recipe below the 95% resolution bar (200 of 202 skipped; measured, documented). Two recipes append: fl-nhs-0001, fl-nhs-0002, with OGL v3.0 attribution in provenance and completed rights records in the manifest. Reheat/safety steps are emitted only where the source itself instructs reheating. Validated: rs lint 27/27 pass, 159/159 lines resolved (100%), 0 unchecked claims, rights clear, ids in scheme. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…n report NHLBI: 54 pages fetched; the Recipe Source credit is parsed per page. Seven pages crediting community manuals (Honoring the Gift of Heart Health, the Filipino and Vietnamese manual family) quarantine fail-closed per SOURCES.md section 3; 47 cookbook-credited pages are eligible, but none clears the 95% quantity-evidence bar (cup-stated vegetables have no suite conversion refs; the best recipe reaches 89%), so 0 recipes are ingested and no invented masses are written. Tooling records every per-page decision in measure mode. REPORT.md: per-source sections for the bundled-keep conversion (25), NHS (2 of 196), NHLBI (0 of 54), MyPlate (unreachable, quarantined fail-closed), and the BBC Good Food cache recommendation: replace both cache files (152 + 3 BBC recipes of full text) with same-shape synthetic fixtures rather than deleting, since generate_rights_audit.py and scrape_recipes.py read them. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…lear) Merges data/recipes/originals/ from overhaul/original-recipes at 607ef99 (fetched read-only, never merged) into corpus.jsonl + rights_manifest.json through the same validation path as licensed sources: - data/recipes/tools/ingest_originals.py re-verifies everything per recipe before it passes through: originals rights manifest 401/401 clear (rights.py); every recipe re-lints clean against suite C at 26c7004 with 0 unchecked claims; id scheme fl-original-<4 digits> unique; dedupe (normalized title, (title, core-ingredient set)) within the batch and against the corpus — 0 stopped, 401/401 accepted. - Linter residue (`_classes`, computed scanner state persisted by the source task's validation in 300 recipes) is stripped at ingestion — validation hygiene, not recipe editing; no other field touched. - rights_manifest.json: one record per ingested recipe (401 new, 428 total): source_id = recipe id, source_type original (260) or original-rewrite (141) per provenance.rewrite_of, license LicenseRef-FL-Original, license_evidence pointing at the originals manifest record, permissions all true except images (no image ships). - Corpus measured (validate_corpus.py, suite at 26c7004): 428 recipes, 2988 lines; 428/428 rights-clear, 0 quarantined; 428/428 lint pass; 2771/2988 lines resolved (92.74%); 0 unchecked, 0 stripped claims; 14/14 bands represented; 78/90 cross-product cells. - Source breakdown: 25 audited seed keepers + 2 NHS + 401 original. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…d corpus Joins data/substitutions/pairs.json (overhaul/substitution-table at 0dc3bdf, fetched read-only and never edited) over the 428-recipe corpus, mirroring the FLSubstitutions package semantics exactly: - data/recipes/tools/apply_substitutions.py resolves corpus ingredient names through the table's alias map (SubstitutionTable semantics: lowercased/trimmed exact match on key, display, or alias), evaluates ContextRules.verdict for each pair's primary and supported_also roles, and accepts verdict=verified only — sourced function AND sourced ratio. Nothing is forced: non-resolving lines get nothing; unknown stays unknown; a conflicting ratio is plausible-at-best and never lands (the shipped egg->mashed-banana conflict verifies only for its liquid supported role, never for binder). - Corpus annotations: 2087 verified (line, pair) entries on 405/428 recipes from 79 of 279 pairs; per-role landings acid=152, aromatic=435, binder=99, fat=447, garnish=167, leavening=8, liquid=493, protein=199, sweetener=68, thickener=34, umami=193. Candidates evaluated over resolving lines: 2296 verified, 3631 plausible, 299 unknown, 248 unsupported — the weaker verdicts are recorded in REPORT.md, never applied. - The annotation is a lean join key (ingredient, pair_id, substitute, verified roles) back into pairs.json, which stays the single source of pair text and evidence; no recipe title/ingredient/step/claim touched. - Re-run is byte-identical (idempotent); full validation stays green (428/428 rights-clear, 0 lint violations, 0 unchecked claims, 2771/2988 resolved, 14/14 bands). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…didate status REPORT.md now covers the integrated corpus end to end: - Combined totals: 428 recipes (25 audited seed keepers + 2 NHS + 401 original), 428/428 rights-clear, 2988 lines (2771 resolved, 92.74%), 1050 verified claims, 14/14 bands, 78/90 cross-product cells, dedupe re-run clean across all 428. - New section 6: originals ingestion — per-recipe re-verification (suite at 26c7004, 0 violations / 0 unchecked / 401/401 accepted), the _classes residue strip, and the 401 new rights records. - New section 7: the accepted-substitutions join — table contract (overhaul/substitution-table at 0dc3bdf, read-only), verified-only application per ContextRules, 2087 landings on 405/428 recipes from 79/279 pairs, candidate verdict accounting, and the full per-pair table (per-recipe mapping is the corpus annotations themselves). - New section 8: the honest 95% shortfall — 2771/2988 = 92.74%; the bar needs 2839 (2838/2988 = 94.98% < 95%), so it fails by 68 resolved lines; the 217 unknown lines sit on 137 distinct staples; the conversion-table fix belongs to eval/scorecard-runner with the operator. No count was massaged and no conversion invented. - Release recommendation: release-candidate pending only the resolution-bar decision; all other suite C bars pass. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…ion result Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…r hold ruling Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Two deterministic tools ground the conversion-evidence work in what the
records actually contain:
- extract_unresolved.py: the exact (ingredient, unit, line-count) worklist
from corpus.jsonl. Measured this session: 217 unresolved lines (all
qty.kind=unknown), 137 distinct pairs, 144 recipes affected — and every
pair's unit is EMPTY: none of the 217 lines carries qty.value, qty.unit,
or a grams key, and no name embeds a digit.
- scan_inrecord_amounts.py: scans each line's own recipe for an amount
stated in the record. Measured: one digit-measure candidate, and it is a
mis-attribution ("soak the breadcrumbs in 1/4 cup milk" — the 1/4 cup is
the milk's); 56 word-measure mentions ("a splash of", "a pinch of") all
measure water, salt, oil or dressing, never the unknown ingredient.
Net: 0 of the 217 lines has an amount stated anywhere in the record. A
conversion row can only convert a stated measure to grams; it cannot
conjure the measure. The annotation deliverable's precondition (amount x
row grams) therefore fails for 217/217 lines — grounding for the REPORT
correction and the operator decision to follow.
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
validate_corpus.py gains the read contract the eval harness adopts via fl-eval-harness-f1 (PR #88): - data/recipes/conversions.json (when present) is integrity-checked per row: id, ingredient, unit, grams > 0, source {name, url, quote, retrieved as YYYY-MM-DD}. - unresolved_pair_stats reports corpus-side evidence coverage: annotated unresolved lines, and which distinct (ingredient, unit) pairs have a citable row — reproducible from the table alone. - pass A lints under the suite's CURRENT semantics (fixture table only); pass B lints under the corpus read path (corpus table joined over the fixture table). Both numbers print, separately and labeled. - A corpus-annotated line that cannot resolve under pass A is the anticipated R2 hard-error class (operator ruling 2026-10-11): reported with its exact message, not gated; every other violation still fails the run. The suite itself is never modified. Self-tested on a scratch copy (clearly-marked fixture row + one annotated line): pass B resolves the line (2772/2988, 92.77%), pass A reports the exact R2 message as anticipated, malformed rows fail field-by-field. Default run unchanged and green: 428/428 rights-clear, 0 claims, 14/14 bands, 2771/2988 (92.74%) under both passes, 0/217 annotated. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…on-evidence pipeline record REPORT.md §8 claimed the 217 unresolved lines were "mostly cup/tbsp/tsp-style household measures" (per the originals task's verification). Direct measurement this session refutes that: all 217 lines carry no value, unit, or grams, no name embeds a digit, and no amount is stated anywhere in their recipes' records (the one digit-measure candidate is a mis-attribution of milk's 1/4 cup to breadcrumbs; all 56 word-measure mentions measure water, salt, oil, or dressing). §8 now states the corrected shape. New §9 records: the three-way reproducible measurement (extractor, in-record scanner, net 0/217 with any stated amount); why the annotation precondition (grams = amount × row grams) fails for all 217 lines — a conversion table cannot conjure a measure, and inventing one would fabricate recipe content (SCHEMA §2, the ruling's own honesty rules); the pipeline as built and self-tested (validator read path under both semantics, row shape, sourcing rules, annotation rule); why no empty conversions.json shipped; both measurements — (a) suite current semantics 2771/2988 = 92.74% unchanged, validation green, (b) corpus-side coverage 0/217 lines, 0/137 staples; the precise blocker (absent quantities live upstream in the originals records, fl-original-recipes-f1, not in any data/recipes/ file) and the path to closing the gap once stated amounts exist; and idempotent re-run instructions. The Limits and handoff bullet points to §9. No measurement is claimed beyond what was run this session; the corpus and the suite are untouched by this commit. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Records the ownership handover: overhaul/original-recipes and data/recipes/originals/ now belong to fl-original-recipes-f1; lists this programme's commits on that branch; voids the 19:10Z fold instruction; notes scope boundaries and the open quantity-gap decision. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…tribution The seed rights records cited only absences: no Source line, present since the initial commit, title not in the BBC scrape cache. None of that shows authorship or permission, and SPEC 3.5 says the 25 non-BBC bundled recipes are not automatically clear. - rights_evidence.py: a shipped recipe needs an affirmative evidence form for its source_type (originals cite their authoring record by id and commit; OGL sources link the licence and the page). Unknown source types and absence-only evidence are refused. Original text may not share 12+ consecutive words with the bundled catalog (measured max on the 401 originals: 9, all stock phrases). - quarantine.py moved the 25 seeds and their records to quarantine.jsonl with the reason; corpus 428 -> 403, manifest 428 -> 403. - validate_corpus.py runs the rights gate and checks the quarantine file. - convert_bundled.py no longer writes the corpus or the manifest. - test_gates.py plants a seed-shaped record and checks it is refused. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
food_safety.py encodes the USDA FSIS Safe Minimum Internal Temperature Chart (fetched 2026-10-11, page updated Apr 14, 2025) and refuses a recipe when any food misses its endpoint: casseroles 165 F, ground poultry 165 F, ground meat 160 F, whole cuts 145 F plus a 3-minute rest, fish 145 F, eggs 160 F, every reheat its own 165 F check. Foods cooked or mixed together reach the strictest of their minimums, so raw egg folded into fish cakes lifts them to 160 F. fix_doneness.py applies 47 reviewed step edits to 36 originals, each asserted against the exact text it replaces: - casseroles and bakes to 165 F (0008, 0102, 0149, 0200, 0235, 0332, 0359) - whole-cut rests added (0023, 0034, 0035, 0094, 0107, 0139, 0225, 0231, 0235, 0239, 0240, 0243, 0247, 0251, 0275, 0276, 0277, 0278, 0284, 0310) - steak no longer pulled at 140 F on carryover (0023) - egg mixtures at 160 F (0014, 0124, 0178, 0287, 0321, 0343) - missing checks added for sausages and rice reheat (0095, 0252, 0309) - picked crab warmed to 165 F (0130) validate_corpus.py runs the endpoint rules; test_gates.py plants each failure shape the 4b6e163 validator accepted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eads A safety check that lives only in step.safety never reaches the cook. The validator now also runs food_safety.text_violations: a step with a check must name the thermometer and the temperature it records, no step may judge doneness by look or touch without a check, and no step may state an internal temperature without one. fix_doneness.py adds 44 text edits across 31 originals: reheat sentences gain "until a food thermometer reads 165 °F (74 °C)", and checks that sat on browning or garnish steps move to the step where the food finishes cooking (0137, 0146, 0171, 0227, 0377, 0396). convert_bundled.py writes the check into the seed step text it attaches it to (the reviewer's lines 324-326); its review output passes the gate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The scanner pinned at 26c7004 skipped ingredient names its alias table did not know and accepted the empty conflict list, so fl-original-0065 claimed dairy-free with "ghee or vegetable oil" in it. 342 of 403 recipes have at least one name the table does not resolve. composition.py refuses a claim when any ingredient's composition is unknown, when a line offers alternatives that are not all known and allowed, when a known ingredient varies by product (stock: milk, wheat, meat; cheese: rennet; tamari, curry powder, miso, chipotle in adobo: wheat or barley; pasta: egg; bread and tortillas: milk, egg, lard), or when a step names a conflicting food the list omits. strip_unproven_claims.py removed 916 of 981 claims; it never adds one. By reason (a claim can have several): unknown composition 881, product-dependent composition 272, conflicting food named in a step 88, alternatives 18. The 65 kept claims also verify under the suite scanner at 9e04af4, which treats unresolved names as unchecked. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The NHS conversion at 4b6e163 rewrote every "no longer pink" to 160 F, including ground poultry; let any earlier check suppress the reheating check; picked egg before poultry in its mixed-dish fallback; gave pork no rest; and emitted classes the schema does not have (meat_pork, meat_red, seafood). build_steps now takes each doneness clause's endpoint from the recipe's own ingredients through food_safety (turkey mince 165 F, pork loin 145 F plus a 3-minute rest, 165 F on a casserole's bake step), gives every reheating step its own 165 F check, adds one closing check that names any food still unchecked at the strictest remaining endpoint, and returns the gate's verdict; a page that fails it is skipped. NHS and NHLBI claims also pass the composition gate, ingest_originals stops a recipe that fails either gate, and method text is HTML-decoded ("don't" in the two NHS records is now "don't"). food_safety.uncovered_classes replaces matching on violation text; "fish pie" is a casserole title. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…heck fed1a05 accepted a rest stated in the step after a whole-cut check, and eight braises got their rest there. The cook reads the rest with the temperature, and suite C at 9e04af4 reads it from the check's own step, so the gate now requires it there and the eight recipes (0225, 0240, 0243, 0275, 0276, 0277, 0278, 0284) carry it in the check step; the following steps get their original text back (0277 drops its now-duplicate "Rest,"). fix_doneness.py keeps one edit per step from the ingested text to the final text; `was` lists outputs committed earlier and since superseded. test_gates.py checks that the edits applied to the originals as ingested at 4b6e163 reproduce the committed steps and that a second pass changes nothing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Totals and the suite C table now describe the 403-recipe corpus: 25 seeds in quarantine, 65 proven claims, 261 thermometer checks, and the resolution gap at 2618/2835 (short 76; it was 68 before the seeds left). New section 10 records each finding's gate, rule source, edit counts, the gate's known approximations, and why the 51 recipes that fail suite C at 9e04af4 are its keyword classification of stock, canned tuna and buns, not missed endpoints. It also corrects the count in 0f39ea8's message: 362 of 403 recipes have a name the alias table cannot resolve, not 342. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fl-original-0152 checked 160 F in the egg white and served the yolk jammy; FoodSafety.gov's egg row says to cook until yolk and white are firm. The endpoint rules now refuse a jammy, runny, soft or molten yolk in any recipe with eggs, and 0152 cooks the eggs until a thermometer in a yolk reads 160 F. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ometer Second review: the text rule matched keywords, so "A thermometer reading 165 F is unnecessary; cook until no longer pink" passed as a check and "Do not rest 3 minutes" passed as a rest. food_safety.affirmative_check now needs one clause that names the thermometer, a measuring verb and the endpoint, is not negated, and does not tie the number to an oven or grill setting. A negation counts when it governs the reading or the rest, so "no longer pink and a thermometer reads 160 °F" still passes. stated_rest ignores negated clauses. Four steps named the thermometer and the number without telling the cook to read it, and are reworded: 0124, 0147, 0375, and 0365, which also took the eggs off at the reading and relied on carryover. 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>
…obv/burst5-fridgeluck-rcp-pipeline
Resolve all 410 corpus ingredient names to 222 canonical keys with pinned USDA FDC rows, compile all 401 recipes into data/recipes/build/recipes-v2.json, and validate the result against the recipe-bundle-v2 contract. - build_vocab.py: surface-name mappings, bulk-CSV nutrient loader (FNDDS and SR Legacy nutrient ids), per-field merge preferring bulk FDC over partial catalog rows, hand-verified pins (mint, salt, egg, salmon, anchovies, avocado, bell pepper, chickpeas, heavy cream) - compile_bundle.py: deterministic bundle emit (pinned generated_at) - validate_bundle.py: schema, reference, fail-closed claim, rights, FSIS and fiber-adjusted Atwater checks; PASS on the full bundle - legacy-ingredient-map.json inherits vocabulary resolutions (egg leaves the salted-frozen-egg row); conforms to ingredient-identity.schema.json - tests: 14 portable pytest tests incl. byte-identical rebuild and validator-catches-seeded-violations - REPORT.md section 11 documents provenance, defects found, and limits Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…nd tests Ship the pipeline compiler's validated bundle (401 recipes from the #113 corpus) at apps/ios/Resources/RecipeCatalog/recipes-v2.json, record provenance and validation evidence under data/recipe-bundle/, and add hosted end-to-end tests that read the shipped bundle: full hydration, v18-upgrade history preservation, and honest fit lines from RecommendationEngine.findRecipes over fixture pantries. Legacy data.json rows are kept deliberately: the spec keeps legacy recipes readable through their existing ids, the loader's successor link requires the legacy row, and the catalog layer has no recommendation query path yet. See data/recipe-bundle/validation-report.md. 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 |
… v1 rewrites 24 FridgeLuck-original recipes (fl-everyday-0001-0024) and 161 RS v1 rewrites of PR #144's legacy rows 167-327 (fl-everyday-0101-0261), assembled by tools/build_slice.py with ingredient-label canonicalization and FSIS safety-class normalization. Checks (all run in the sandbox): - tools/check_everyday.py: 0 errors (ingredient resolution 100%, household grams, category/diet balance, step integrity, dedupe vs the #113 corpus and legacy #144 rows, step-text 12-gram overlap) - pinned suite-C validator (revision 26c7004) via the in-process adapter: VALIDATION GREEN, 185 recipes, bands 14/14 - tools/test_check_everyday.py: 23 tests OK Rights: rights_manifest.json + evidence/rights.json regenerated for all 185 recipes (LicenseRef-FL-Original); evidence/ carries the pinned allergen/conversion tables from the suite revision. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Author 179 original recipes across East Asia (57), Southeast Asia (33), MENA (31), and the Americas/Europe (58) as RS v1 records with record-carried FNDDS conversions, FSIS-derived safety checks, and a packet-local rights manifest. Includes the compiler, FNDDS registry, packet checker with recomputed quantity evidence, 14 planted-failure tests, README, and validation report. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…ests try DatabaseQueue(), try NutritionService, @mainactor makeEngine, and try await db.read in async test bodies (GRDB 7 resolves read to its async overload in async contexts). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…als-world' into obv/burst5-fridgeluck-rcp-integrate
…als-everyday' into obv/burst5-fridgeluck-rcp-integrate
Ingest the world-2026-10 (177/179) and everyday-2026-10 (185) packets into the corpus: packet ids renumbered into the fl-original-* band with the mapping recorded in data/recipes/evidence/ingest-2026-10-map.json, rights manifests converted to the main shape, two title-collision recipes excluded (never retitled). Vocabulary rebuilt with build_vocab (additive: per_100g/fdc_id unchanged for existing keys); bundle recompiled deterministically — 763 recipes rcp-000167..rcp-000934, 329 ingredients, validator PASS, 14/14 pipeline tests (corpus-size pin 401 -> 763, GENERATED_AT bumped per the tool's recompile instruction). Everyday categories stay packet-level metadata: the bundle-v2 collections enum does not cover them (spec-owned contract, PR #148). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…' into obv/burst5-fridgeluck-rcp-integrate
searchedIngredientCount reports the raw scanned set; the staple fallback shapes the query through RecommendationPolicy, not the published count. Also merge the current loader head (72e5d96) so the loader tests run the fixed FK-seeding fixtures. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
|
@coderabbitai review |
|
|
Independent review (Obvious, Autobuild + Max) Verdict: changes needed The shipped bundle itself checks out: Findings1. HIGH — the bundle validator's dietary-claim and FSIS gates read a vocabulary field that doesn't exist, so claims validation fails open. 2. HIGH — the committed corpus fails the pipeline's own release gate: 367 errors, covering all 362 ingested packet recipes. 3. MEDIUM — the byte-identity test cannot pass on a fresh clone; the manifest's cited verification is environment-dependent. 4. MEDIUM — the world packet's own checker can't even be imported from the committed tree. 5. LOW — none of the Python suites run in CI. (Informational, no action needed: What I verified (commands and results)
What I could not verify
Nothing here blocks the data itself — the bundle, manifest, mapping, and CI state are solid. Findings 1–2 are gate-integrity gaps that the repo's own contracts treat as ship-blocking, so changes needed before this leaves draft. |
…portable determinism, Python CI - validate_bundle.py: diet-claim and FSIS audits read the vocabulary's real per-key classes list (the old singular 'class' lookup never resolved, so the gates never fired; vegan blocker named a nonexistent dairy_egg class). Claims now fail on expressible composition contradictions (vegetarian blocks meat/fish/shellfish; vegan adds animal/dairy/egg/honey); claim lines with no known vocabulary classes are disclosed as warnings — the corpus composition gate remains the fail-closed authority. Seeded vegetarian-on-chicken now fails; the shipped bundle passes with 32 disclosed warnings. - test_pipeline.py: byte-identity no longer needs operator /tmp inputs; it proves the committed build copy equals a fresh compile. Vocabulary-rebuild determinism stays available as a skip-unless-inputs-present operator check. Corpus-size pin 763 -> 762. - validate_corpus.py + rights evidence: locate packet-ingested rights records by source_ref with corpus-id fallback, pin packet evidence at recorded commits, and run against the corpus's pinned 26c7004 suite (committed under data/recipes/suite/). Release gate on the committed tree: VALIDATION GREEN, 762 recipes, 401/401 linter, 0 unchecked claims. - world packet: checker suite modules (bands/rs/rights, allergens, canon) and the FNDDS portion evidence committed under the packet's src/ so the checker runs on a fresh clone; packet checks PASS (179 records). - corpus food safety: thermometer checks on Chicken Kyiv, Bigos, Leczo; unsupported internal-temperature claim removed from Koose; Curry Goat (fl-world-0135) excluded fail-closed — no FSIS goat row. Corpus is 762. - .github/workflows/python-ci.yml: pipeline tests, release gates, packet checker, and byte-identity run on every PR. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
The CommittedCorpus gates' suite loader fell through to a git-show of the 26c7004 pin when FL_SUITE_DIR was unset, which a fresh clone cannot resolve. The pinned suite is committed in-tree this round (data/recipes/suite/, provenance documented); prefer it, keeping FL_SUITE_DIR and the git-show as overrides. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…s git-show The gates verify the committed tree against pinned past pipeline states (corpus era 4b6e163, originals rights 607ef99, packet commits 1733f5f and 68c164d). A shallow PR checkout does not contain those objects; fetch them by sha so the pins stay real revisions rather than in-tree copies. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
What changed and why
Integrates the recipe-catalog slices into one shippable state, on top of the loader PR's branch (
obv/burst5-fridgeluck-rcp-loader):apps/ios/Resources/RecipeCatalog/recipes-v2.json— 762 recipes (rcp-000167–rcp-000934), 328 ingredients, sha25696d9896a…d8439, 2,600,354 bytes. Corpus: PR Rights-approved recipe corpus: 401 originals behind FSIS safety, rights and claim gates #113's 401 recipes + the world packet (PR feat(data): add world-2026-10 recipe originals packet (179 recipes) #164, 176/179 ingested) + the everyday packet (PR feat(data): add everyday-2026-10 recipe slice — 24 originals + 161 RS v1 rewrites #163, 24 originals + 161 re-authored legacy-feat(data): expand bundled recipes to 327 and add bundled-data validator #144 rewrites). The app target's existing resource glob picks it up with noproject.ymlchange; the loader prefers it over legacydata.jsonat launch.data/recipe-bundle/ingest_packets_2026_10.py+ mapping atdata/recipes/evidence/ingest-2026-10-map.json): packet ids renumbered into the corpus'sfl-original-*band (the compiler derives catalog ids from the id suffix — packet suffixes would collide), rights manifests converted to the main shape with evidence pointing at the packet files and commits. Three world recipes were excluded, never retitled or re-authored — "Egg Drop Soup" and "Chicken Enchiladas Rojas" are normalized-title collisions with existing corpus dishes that are different recipes (dedupe gate), and "Curry Goat" failed the fail-closed FSIS gate (the chart has no goat row, so its protein cannot be verified); all three exclusions are recorded in the mapping file.build_vocab.py: 329 keys, 555 surfaces, +97 FDC rows.per_100g/fdc_idunchanged for all 220 pre-existing keys (verified); 97 new world-cuisine keys carry honest partial nutrition coverage rather than synthesized values.data/recipe-bundle/: provenance, contents (rights 460 original / 302 quarantined, nutrition 163 computed / 345 estimated / 254 partial, 255 step timers), and every command run with its result.Tests/Integration/RecipeCatalogIntegrationTests.swift) reading the shipped bundle fromBundle.main, not a fixture: full hydration with lines/steps/nutrition and resolvable ingredient keys, launch idempotence, v18-upgrade history preservation with no false legacy links,RecommendationEngine.findRecipesover fixture pantries with honest fit lines, and the catalog read path serving structured content.0ba4be1) — all five findings from review comment 6199645081 addressed:classfield (the vocabulary carries a plural per-keyclasseslist), and the vegan blocker named a class (dairy_egg) that does not exist — so the gates could never fire. Reproduced before fixing: seedingvegetarian: trueonrcp-000177(containschicken_breast) returned PASS. Now the audits read the realclasses: claims fail on expressible composition contradictions (vegetarian blocks meat/fish/shellfish — the vocabulary'sanimalentries are ghee, honey, mayonnaise, which are lacto-ovo-vegetarian-acceptable; vegan additionally blocksanimal/dairy/egg/honey), thermometer endpoints stay meat/fish-gated, and claim lines with no known vocabulary classes are disclosed as warnings, not silently passed — the corpus-side composition gate remains the fail-closed authority (verified green: 762 recipes, 0 unchecked claims). Verified after fixing: the shipped bundle PASSES with 32 disclosed warnings, and the same seeded vegetarian-on-chicken mutation now FAILS naming the contradiction.source_reffirst with id fallback), packet evidence wasn't pinnable at its recorded commits (now pinned via extendedrights_evidence.py/pin_rights_evidence.py), and the validator needed suite inputs that weren't in the tree (the corpus's authoring-era26c7004suite is now committed underdata/recipes/suite/, separate from the packet's9d3a704suite under the packet'ssrc/). The five food-safety failures were fixed at the recipe level (thermometer checks on Chicken Kyiv, Bigos, Leczo; an unsupported internal-temperature claim removed from Koose) or excluded fail-closed (Curry Goat). Release gate on the committed tree: VALIDATION GREEN — 762 recipes, linter 401/401, 0 unchecked claims, 94.48% quantity resolution, 14/14 bands, rights-clear./tmpinputs (fixed). The test ranbuild_vocab.py, which reads pinned external downloads that cannot be committed (USDA FDC bulk CSVs, canon). It now proves the property that matters on a fresh clone: the committed build copy equals a freshcompile_bundle.pyoutput byte-for-byte. Vocabulary-rebuild determinism remains as a separate check that skips unless the operator has the pinned inputs.bands.py,rs.py,rights.py,allergens.json, pinned canon, and the FNDDS portion evidence are committed under the packet'ssrc/; the checker now runs from a clean checkout and PASSES (179 records; all 14 previously planted mutations still caught during development)..github/workflows/python-ci.ymlruns on every PR: the pipeline + gate test suites, the corpus release gate, the world packet checker, bundle validation, and byte-identity (fresh compile must equal the committed build copy). First hosted runs exposed two more fresh-clone gaps, fixed in follow-ups: the gates' suite loader now reads the committed pinned suite first (c6d94cb), and the reproducibility gates' pinned revisions (corpus era4b6e163, originals rights607ef99, packet commits1733f5f7/68c164de) are fetched by sha in the workflow so the pins stay real revisions, not in-tree copies (edb6975).How I checked it
Fix round (head
0ba4be1), all run on the committed tree:python3 -m pytest data/recipes/tools/tests/test_pipeline.py data/recipes/tools/test_gates.py -q→ 68 passed, 1 skipped (the skip is the operator-only vocabulary-rebuild determinism check).vegetarian: trueonrcp-000177→ FAIL (1 problem): claims vegetarian but line 'chicken_breast' has class(es) ['meat']; clean bundle → PASS with 32 disclosed warnings.python3 data/recipes/tools/validate_corpus.py→ VALIDATION GREEN (762 recipes, 401/401 linter, 0 unchecked claims, 94.48% resolved, 14/14 bands).python3 data/recipe-sources/world-2026-10/src/check_packet.py --quiet→ PASS (179 records).python3 data/recipes/tools/validate_bundle.py→ PASS (32 disclosed warnings).compile_bundle.py→ identical (96d9896acdaf7753…).Original round: ingest → 361 ingested / 3 excluded;
build_vocab.py→ 329 keys / 555 surfaces, nutrition fields stable on existing keys;GENERATED_ATpinned2026-10-15T12:45:00Zper the compiler's recompile instruction.What I could NOT check
ac5a535must be re-confirmed at0ba4be1.Hosted CI (macOS + Python)
ac5a535(pre-fix): build-and-test PASS (run 38735419109) and dead-code-advisory PASS.cbd3268failed in the test suite: three loader-test failures plus one integration-test assertion — the branch was cut from loader81bd03dwhile PR feat(ios): hydrate recipe bundle-v2 catalog with legacy fallback and revision history #151 gained the fixes upstream (72e5d96), andsearchedIngredientCountreports the raw scanned set (0 for an empty pantry). Fixed by merging loader head and correcting the assertion.edb6975(all green):build-and-testPASS 7m28s anddead-code-advisoryPASS 3m52s (run 38803890988).python-ci(pipeline-tests-and-gates) PASS 22s (run 38803890897) — pipeline + gate tests, corpus release gate, world packet checker, bundle validation, and byte-identity all green on a fresh shallow clone.Assumptions
data.jsonare kept, deliberately: the spec keeps legacy recipes readable through their existing ids, the loader's successor-link requires the legacyrecipesrow, andRecommendationEnginequeries the legacy tables — the catalog layer has no recommendation query path yet. The everyday packet's 161 re-authored legacy-feat(data): expand bundled recipes to 327 and add bundled-data validator #144 rewrites ship as new catalog rows, not in-place successors; deleting the 166 legacy rows waits for migrated successors for all of them plus a catalog-backed recommendation path. Details indata/recipe-bundle/validation-report.md.collectionsenum (quick,budget,one_pot,comfort,use_soon) does not cover them; widening a spec-owned contract (PR feat(spec): recipe bundle-v2, ingredient identity, and taste-event contracts #148) is not an integration call.🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact