Skip to content

feat(data): integrate recipe bundle v2 — ship compiled catalog, manifest, and end-to-end coverage - #162

Draft
obvious-autobuild[bot] wants to merge 42 commits into
obv/burst5-fridgeluck-rcp-loaderfrom
obv/burst5-fridgeluck-rcp-integrate
Draft

obvious-autobuild[bot] wants to merge 42 commits into
obv/burst5-fridgeluck-rcp-loaderfrom
obv/burst5-fridgeluck-rcp-integrate

Conversation

@obvious-autobuild

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

Copy link
Copy Markdown

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):

  • Ships the compiled bundle. apps/ios/Resources/RecipeCatalog/recipes-v2.json — 762 recipes (rcp-000167–rcp-000934), 328 ingredients, sha256 96d9896a…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 no project.yml change; the loader prefers it over legacy data.json at launch.
  • Packet ingestion (data/recipe-bundle/ingest_packets_2026_10.py + mapping at data/recipes/evidence/ingest-2026-10-map.json): packet ids renumbered into the corpus's fl-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.
  • Vocabulary rebuilt additively with the pipeline's own build_vocab.py: 329 keys, 555 surfaces, +97 FDC rows. per_100g/fdc_id unchanged for all 220 pre-existing keys (verified); 97 new world-cuisine keys carry honest partial nutrition coverage rather than synthesized values.
  • Manifest and validation report under 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.
  • End-to-end tests (Tests/Integration/RecipeCatalogIntegrationTests.swift) reading the shipped bundle from Bundle.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.findRecipes over fixture pantries with honest fit lines, and the catalog read path serving structured content.
  • Review-fix round (2026-10-15, head 0ba4be1) — all five findings from review comment 6199645081 addressed:
    1. Bundle-validator fail-open claims (fixed). The diet-claim and FSIS audits read a nonexistent singular class field (the vocabulary carries a plural per-key classes list), and the vegan blocker named a class (dairy_egg) that does not exist — so the gates could never fire. Reproduced before fixing: seeding vegetarian: true on rcp-000177 (contains chicken_breast) returned PASS. Now the audits read the real classes: claims fail on expressible composition contradictions (vegetarian blocks meat/fish/shellfish — the vocabulary's animal entries are ghee, honey, mayonnaise, which are lacto-ovo-vegetarian-acceptable; vegan additionally blocks animal/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.
    2. Release-gate 367 errors on the committed tree (fixed). Root causes: rights records for the 361 packet-ingested recipes were looked up by their re-keyed corpus ids only (now matched by packet source_ref first with id fallback), packet evidence wasn't pinnable at its recorded commits (now pinned via extended rights_evidence.py/pin_rights_evidence.py), and the validator needed suite inputs that weren't in the tree (the corpus's authoring-era 26c7004 suite is now committed under data/recipes/suite/, separate from the packet's 9d3a704 suite under the packet's src/). 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.
    3. Byte-identity test needing operator /tmp inputs (fixed). The test ran build_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 fresh compile_bundle.py output byte-for-byte. Vocabulary-rebuild determinism remains as a separate check that skips unless the operator has the pinned inputs.
    4. Missing packet-checker modules on a fresh clone (fixed). bands.py, rs.py, rights.py, allergens.json, pinned canon, and the FNDDS portion evidence are committed under the packet's src/; the checker now runs from a clean checkout and PASSES (179 records; all 14 previously planted mutations still caught during development).
    5. Python CI absent (fixed). New .github/workflows/python-ci.yml runs 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 era 4b6e163, originals rights 607ef99, packet commits 1733f5f7/68c164de) are fetched by sha in the workflow so the pins stay real revisions, not in-tree copies (edb6975).
  • Merged the pipeline and packet branches into this branch so corpus, compiler, vocabulary, and bundle are self-consistent.

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).
  • Seeded-negative control on the claim audit: vegetarian: true on rcp-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).
  • Byte-identity: sha256 of the build copy before/after a fresh compile_bundle.py → identical (96d9896acdaf7753…).
  • Shipped resource == build copy (byte-compare), corpus 762 = bundle 762, test pin updated to 762.

Original round: ingest → 361 ingested / 3 excluded; build_vocab.py → 329 keys / 555 surfaces, nutrition fields stable on existing keys; GENERATED_AT pinned 2026-10-15T12:45:00Z per the compiler's recompile instruction.

What I could NOT check

  • Nothing Swift was compiled or run locally (no toolchain in this environment). The test target's real compile is the hosted macOS CI on this PR. The bundle changed in this round (762 recipes), so the previously green macOS run at ac5a535 must be re-confirmed at 0ba4be1.
  • iOS device behavior (bundle load timing, resource packaging on device) is unverified; CI's simulator suite is the evidence. The byte-identity step's diff-vs-HEAD semantics were verified by hand before its first run (CI checkout makes the worktree equal the committed head), and it passed on CI.
  • iOS device behavior (bundle load timing, resource packaging on device) is unverified; CI's simulator suite is the evidence.

Hosted CI (macOS + Python)

  • At ac5a535 (pre-fix): build-and-test PASS (run 38735419109) and dead-code-advisory PASS.
  • First run at cbd3268 failed in the test suite: three loader-test failures plus one integration-test assertion — the branch was cut from loader 81bd03d while PR feat(ios): hydrate recipe bundle-v2 catalog with legacy fallback and revision history #151 gained the fixes upstream (72e5d96), and searchedIngredientCount reports the raw scanned set (0 for an empty pantry). Fixed by merging loader head and correcting the assertion.
  • Fix round, final states at head edb6975 (all green):
    • macOS build-and-test PASS 7m28s and dead-code-advisory PASS 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

  • Legacy rows in data.json are kept, deliberately: the spec keeps legacy recipes readable through their existing ids, the loader's successor-link requires the legacy recipes row, and RecommendationEngine queries 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 in data/recipe-bundle/validation-report.md.
  • Everyday categories stay packet-level metadata: the bundle-v2 collections enum (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.
  • Claims with unknown vocabulary classes are disclosed, not blocked: the bundle-level audit catches contradictions expressible in the vocabulary; the corpus composition gate (fixture classes over ingredient names) is the fail-closed authority for claims, and it is green for all 23 claim-bearing recipes.
  • Curry Goat excluded, not re-authored: the fail-closed FSIS chart has no goat row; substituting a different protein would re-author packet content, which the packet-ingestion contract forbids.
  • Draft PR — not for merge without human review.
  • Human author: Sam Gu (sgu07966@gmail.com).

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

SamGu-NRX and others added 30 commits October 11, 2026 14:26
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&#x27;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>
7e946b2 reports a missing reading only when the step names a thermometer,
so the metadata-only planted case now gets one message, not two. 7e946b2
was pushed with this test failing.

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

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

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

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

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

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

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

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

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

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

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

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 314e91fd-476e-4c80-9e76-927266722e39

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

Use the checkbox below for a quick retry:

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

SamGu-NRX and others added 2 commits October 15, 2026 11:35
… 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>
Manik678 and others added 6 commits October 15, 2026 11:46
…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>
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>
@SamGu-NRX

Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review skipped.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@obvious-autobuild

Copy link
Copy Markdown
Author

Independent review (Obvious, Autobuild + Max)

Verdict: changes needed

The shipped bundle itself checks out: recipes-v2.json is byte-identical to a fresh double compile (87f4f2c1…99eb7f, 2,603,827 bytes), every manifest number I recomputed matches exactly (763 recipes / 329 ingredients, band rcp-000167..rcp-000934, coverage 163/345/255, rights 461/302, timers 258, all six cuisine counts), the two documented exclusions are real title collisions recorded in the ingest map, the ingest script refuses to re-run, and macOS CI is green at ac5a535 (run 38735419109: build-and-test 5m0s, dead-code-advisory 2m59s). The problems are in the gates around the bundle: two of the pipeline's own safety nets fail open or fail on the committed tree, and the Python suite is not reproducible from a fresh checkout.

Findings

1. HIGH — the bundle validator's dietary-claim and FSIS gates read a vocabulary field that doesn't exist, so claims validation fails open.
data/recipes/tools/validate_bundle.py:78 builds classes = {e["key"]: e.get("class") …}, but all 329 entries in data/ingredients/vocabulary.json carry classes (plural, a list) and zero carry class — so classes.get(key) is None for every ingredient. Evidence: I seeded vegetarian: true onto rcp-000177 ("Sunday Roast Chicken Salad Wrap", line chicken_breast) and the validator prints PASS; rebuilding the map as {key: set(entry["classes"])} and intersecting flags exactly that line (blocked = ['chicken_breast']). The same broken map drives the FSIS check at line 103 (has_protein is always False), so the "thermometer endpoints only on meat/fish recipes" gate can never fire as specified either. The shipped 23 claim-bearing recipes happen to contain no real claim/class collision (checked against the real classes data), so nothing bad is in this bundle — but the gate that exists to catch the next bad claim is decorative, and test_pipeline.py seeds violations for dangling keys and kcal but not for claims, which is why this survives. Smallest fix: at line 78 build {e["key"]: set(e.get("classes") or [])} and intersect at lines 91 and 103; add a seeded-claim violation test alongside the two existing seeded tests. (This is the same root cause flagged in the #158 pipeline review — it carried into this branch unchanged.)

2. HIGH — the committed corpus fails the pipeline's own release gate: 367 errors, covering all 362 ingested packet recipes.
pytest data/recipes/tools/test_gates.py::CommittedCorpus::test_release_gates fails on this PR's head with 367 errors: 362 × "no rights record" (every packet ingest, fl-original-0405..0768) plus 5 food-safety failures in world recipes (fl-original-0539 'goat meat' has no row in the FSIS chart; fl-original-0565 egg check is metadata only; fl-original-0566 and fl-original-0574 smoked sausage needs a thermometer check; fl-original-0581 step[2] states an internal temperature without a proper check). Root cause of the 362: validate_corpus.py:115 resolves rights as records.get(r.get("source_ref")) — the pre-integration corpus has source_ref == id (fl-original-0001), but ingested packet recipes keep packet refs (fl-world:gochujang-chicken-lettuce-wraps, …) while ingest_packets_2026_10.py keys the converted manifest records by corpus id: 0/362 lookups hit. data/recipes/evidence/rights.json (the pinned evidence the gate cross-checks) covers 0 packet recipes. Net effect: 362 of the 763 shipped recipes carry rights statuses that the pipeline's own standard — "nothing ships without verified affirmative evidence" — does not consider verified. Smallest fix: at ingest, also key the converted record under the recipe's source_ref (or make the gate fall back to the recipe id), regenerate pinned evidence for the packet citations, and fix the 5 world recipes through the packet's own checker. None of this changes the shipped JSON's bytes, but per the repo's own gates the bundle shouldn't ship while these fail.

3. MEDIUM — the byte-identity test cannot pass on a fresh clone; the manifest's cited verification is environment-dependent.
test_pipeline.py:115 (test_bundle_compiles_byte_identically) runs build_vocab.py first; that tool hard-reads /tmp/canon/identities.json and /tmp/fdc/… (build_vocab.py:12-13,42-43), which exist only in the authoring environment. On a fresh checkout: FileNotFoundError → the suite is 1 failed, 13 passed, not the "14 passed" manifest.json cites. The determinism property itself is real — compile_bundle.py twice from committed inputs reproduces 87f4f2c1…99eb7f exactly — but the test that proves it can't run here. Smallest fix: make that test compile-only (vocabulary.json is committed; the compile is the deterministic artifact), or commit/portable-ize build_vocab's inputs.

4. MEDIUM — the world packet's own checker can't even be imported from the committed tree.
check_packet.py:48 does import bands; no bands.py exists anywhere in the repo (it is also imported by ingest_originals.py:88 and validate_corpus.py:232 via an uncommitted suite dir). pytest data/recipe-sources/world-2026-10/src/test_check_packet.py errors at collection: ModuleNotFoundError: No module named 'bands'. The validation report says corpus-level linting "ran through the packets' own checkers" — that claim is not reproducible from this tree, and finding 2's five safety failures are exactly the kind of thing that checker exists to catch. Smallest fix: commit the suite modules (bands.py, plus the rights.py/rs.py that validate_corpus.py also expects) under the packet's src/.

5. LOW — none of the Python suites run in CI. .github/workflows only builds and tests the Swift app, so the green checks on this PR never execute test_gates.py, test_pipeline.py, or the packet checkers. Combined with the body's "How I checked" listing only the passing subset, a reader can't tell that the release gate fails on the committed tree. Suggest one line in the body naming the failing suites — and, better, a lightweight pytest job.

(Informational, no action needed: data/recipes/conversions.json is absent; load_corpus_conversions tolerates that by design and the report already discloses it.)

What I verified (commands and results)

  • gh pr checks 162 → build-and-test pass 5m0s, dead-code-advisory pass 2m59s (run 38735419109 at ac5a535).
  • python3 data/recipes/tools/compile_bundle.py twice → byte-identical output, sha256 87f4f2c1…99eb7f both runs; sha256sum of the shipped resource vs data/recipes/build/recipes-v2.json → identical (2,603,827 bytes each).
  • Manifest cross-check against the shipped bundle: 763 recipes, 329 ingredients, unique ids rcp-000167..rcp-000934, coverage {computed 163, estimated 345, partial 255}, rights {original 461, quarantined 302}, timers 258, cuisines {asian 191, american 161, mediterranean 132, italian 105, mexican 98, indian 76} — all match manifest.json exactly.
  • ingest-2026-10-map.json: 362 mapped; exclusions exactly fl-world-0043 "Egg Drop Soup" and fl-world-0147 "Chicken Enchiladas Rojas" (title collision) — matches the body.
  • python3 data/recipe-bundle/ingest_packets_2026_10.py (dry run) on the integrated corpus → "refusing: packet ids already present in the corpus", exit 1 — idempotence holds.
  • Suites: test_pipeline.py 1 failed / 13 passed (finding 3); test_gates.py 1 failed / 53 passed (finding 2); everyday checker test_check_everyday.py 23 passed; world test_check_packet.py collection error (finding 4).
  • Claim/class audit with the real vocabulary data: 0 collisions among the 23 claim-bearing recipes.
  • Revert-test: with corpus.jsonl reverted to the pre-integration 401-recipe version (from 7fc68d4), test_bundle_covers_whole_corpus fails (pin 763 vs 401) — the updated pin genuinely exercises this change; corpus restored afterward.
  • Fail-open repro: seeded vegetarian: true on rcp-000177 → validator PASS; corrected lookup flags chicken_breast (finding 1).
  • Read the new integration tests in full: they read the shipped bundle via Bundle.main, assert launch idempotence, the v18-upgrade history case, honest fit lines, and the empty-pantry raw-count assertion (searchedIngredientCount == 0) the body describes.

What I could not verify

  • Anything Swift: no macOS toolchain here, so the integration tests were reviewed as source and their green state taken from hosted CI run 38735419109 (which passed) — consistent with the PR's own disclosure.
  • The authoring-environment claims ("14 passed"; corpus linting "ran through the packets' own checkers") are not reproducible from the committed tree (findings 3–4).
  • Bundle-size impact beyond file bytes, and GRDB 7 runtime behavior of the new tests beyond what CI covers.

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.

SamGu-NRX and others added 3 commits October 15, 2026 20:32
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants