Skip to content

Rights-approved recipe corpus: 401 originals behind FSIS safety, rights and claim gates - #113

Merged
SamGu-NRX merged 27 commits into
alphafrom
fleet/fl-recipe-corpus-v2
Oct 16, 2026
Merged

SamGu-NRX merged 27 commits into
alphafrom
fleet/fl-recipe-corpus-v2

Conversation

@SamGu-NRX

@SamGu-NRX SamGu-NRX commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

What changed

This PR adds FridgeLuck's rights-approved recipe corpus: data/recipes/corpus.jsonl (401 recipes in the RS v1 schema from data/recipes/SCHEMA.md), its rights manifest, the ingestion and validation tools under data/recipes/tools/, and data/recipes/REPORT.md. All 401 are originals written for FridgeLuck by fl-original-recipes-f1. Ingestion validates every recipe with suite C's rs.py, rights.py and bands.py, joins the accepted substitutions, and runs four gates of its own that the reviews asked for. Recipes that fail a gate wait in data/recipes/quarantine.jsonl with the reason.

The review at 4b6e163 blocked on five findings, and the re-review at 0f39ea8 found four gaps in the first fixes. Each is fixed in the data and enforced by validate_corpus.py, with planted failures in data/recipes/tools/test_gates.py that the 4b6e163 validator accepted.

Doneness endpoints follow the FSIS chart (finding 1; re-review 3)

food_safety.py encodes the USDA FSIS Safe Minimum Internal Temperature Chart, fetched 2026-10-11 (page last updated Apr 14, 2025). The rows it encodes are quoted in FSIS_ROWS, and each class pattern cites its row. The gate refuses a recipe when any food misses its endpoint:

  • Casseroles, stuffing, reheated leftovers, precooked meat and ham must reach 165 °F (74 °C).
  • Ground poultry is 165 °F, ground meat 160 °F, and fish and shellfish 145 °F.
  • Whole cuts of beef, pork, veal and lamb are 145 °F (63 °C), with a 3-minute rest stated in the same step.
  • Eggs are 160 °F, with yolk and white firm.
  • Foods cooked or mixed together reach the strictest of their minimums. Raw egg folded into fish makes the cakes an egg dish.
  • Every reheating step carries its own 165 °F check, whatever was checked before it.
  • An animal food with no row in the chart refuses the recipe rather than getting a guessed endpoint. Venison, rabbit and organ meats other than poultry giblets fall here.

fix_doneness.py applies the corrections, each asserted against the exact text it replaces; 89 steps in 74 originals changed. Seven casseroles and bakes went to 165 °F, including the breakfast casserole (fl-original-0008, was 160 °F) and the tuna gratin (0102, was a 145 °F fish check). Twenty whole-cut checks gained a rest, including the citrus pork (0094). The cod cakes (0343) had relied on the 145 °F poach after raw egg went in, and now cook to 160 °F. A flank steak (0023) was pulled at 140 °F on carryover, and the chana masala (0152) served a jammy yolk under a 160 °F reading of the white.

Checks are instructions in the step text (finding 2; re-review 1)

A check that lives only in step.safety never reaches the cook. Every step with a check now tells the cook, in one clause, to read the endpoint on a thermometer in the food, for example "Reheat leftovers until a food thermometer reads 165 °F (74 °C)." Keywords are not enough. "A thermometer reading 165 F is unnecessary" fails because the reading is negated, an oven setting does not count as a reading, and "do not rest 3 minutes" is not a rest. No step judges doneness by look or states an internal temperature without a check. Six checks that sat on browning or garnish steps moved to the step where the food finishes cooking. convert_bundled.py (the reviewer's lines 324-326) now writes the check into the seed step text too.

Rights rest on pinned, affirmative evidence (finding 3; re-review 4)

The 25 bundled seeds cited only absences: no Source line, present since the initial commit, title not in the BBC scrape cache. None of that shows who wrote them or that anyone gave permission, and SPEC 3.5 says these 25 are not automatically clear. They moved to quarantine with their old records.

rights_evidence.py no longer trusts how a citation is spelled. pin_rights_evidence.py writes data/recipes/evidence/rights.json from git: every 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. An original clears only when its record exists at the cited commit and carries the licence, the app's permissions and this recipe's identity. An external source clears only with a pinned "verified" verdict, and a test re-derives the evidence file from git.

Checking the NHS terms changed their verdict. The two NHS Healthier Families recipes link their own Terms of Website Use, which say "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 cited the general nhs.uk OGL release instead. With the two in conflict the permissions are unknown, so both NHS recipes moved to quarantine.

Claims need known composition (finding 4; re-review 2)

The scanner pinned at 26c7004 skipped ingredient names its alias table did not know, so fl-original-0065 claimed dairy-free with "ghee or vegetable oil" in it. composition.py refuses a claim in four cases:

  • An ingredient's composition is unknown to suite C's allergens.json.
  • A line offers alternatives that are not all known and allowed.
  • A known ingredient varies by product: stock (meat, milk, wheat), cheese (rennet), tamari, curry powder and miso (wheat or barley), pasta (egg), and bread (milk, egg, lard).
  • A step names a food the claim forbids. "Peanut butter" is exempt from the dairy-free scan only, so it still fails nut-free. Compound foods such as pesto, mayonnaise and Worcestershire block every claim they contradict.

strip_unproven_claims.py removed 916 of 981 claims and never adds one. Most come back only when alias rows or the FLIngredientFacts package cover the 362 recipes with an unresolved name. (The 0f39ea8 commit message says 342; the measured count is 362.)

The NHS conversion uses the same rules (finding 5)

The NHS conversion rewrote every "no longer pink" to 160 °F, including ground poultry. Any earlier check suppressed the reheating check, the mixed-dish fallback chose egg over poultry, and pork got no rest. It also wrote classes the schema does not have (meat_pork, meat_red, seafood). ingest_nhs.py build_steps now takes each endpoint from the recipe's ingredients through food_safety.py. It checks every reheating step at 165 °F, closes with a check naming any food still unchecked, and skips a page that fails the gate. NHS and NHLBI claims pass the composition gate, and ingest_originals.py stops a recipe that fails either gate.

Why

SPEC 3.1 makes thermometer doneness from FSIS a release blocker, and SPEC 3.5 says unknown rights means quarantine and claims must be checked. Suite C's linter at 26c7004 passed every case above, so the corpus needs its own gates; the operator ruled that nothing under evaluation/ changes. The counts show the cost: 27 fewer recipes and 916 fewer claims, because neither could be proven.

Proof

Run at head 7fc68d48e35f14cce3df1a08c37657f18b738d6a under the long-run heavy lease (lr-lease acquire --kind heavy, lockf -k ~/.long-run/locks/heavy.lock). Suite C was extracted read-only from 26c7004 with git archive 26c7004 evaluation/suites/recipes | tar -x -C /tmp/fl113-suite-26c.

Unit tests (54 planted-failure and corpus tests):

$ python3 -I -m unittest discover -s data/recipes/tools -p 'test_*.py'
Ran 54 tests in 0.253s
OK

Validator (the list of 137 uncovered conversion pairs is omitted):

$ python3 -I data/recipes/tools/validate_corpus.py --suite-dir /tmp/fl113-suite-26c/evaluation/suites/recipes
corpus: 401 recipes, 2829 ingredient lines
rights: 401 rights-clear, 0 quarantined-in-corpus (must be 0)
accepted: 401 recipes in corpus.jsonl; quarantined: 27 in quarantine.jsonl (rights 27)
release gates: 0 violation(s) (rights evidence, original-text overlap, FSIS endpoints, thermometer in step text, claim composition)
claims shipped: 65
linter: 401/401 recipes pass; quantity evidence: 2612/2829 lines resolved (92.33%)
claims: 0 unchecked, 0 stripped by the linter
pass A (suite current semantics): 2612/2829 lines resolved (92.33%)
pass B (corpus read path):        2612/2829 lines resolved (92.33%)
  bands represented: 14/14
  cross-product cells filled: 75/90 (informational)
VALIDATION GREEN: rights-clear corpus lints clean, claims verified, ids unique and in-scheme, no quarantined recipe in corpus.jsonl
measured: recipes=401 resolved_pct=92.33 bands=14/14
exit=0

Every correction tool is a no-op on the committed files, and the pinned evidence re-derives from git:

$ python3 -I data/recipes/tools/fix_doneness.py
96 edits; 0 recipe(s) changed
food-safety rules: 0 recipe(s) still failing
$ python3 -I data/recipes/tools/strip_unproven_claims.py --suite-dir /tmp/fl113-suite-26c/evaluation/suites/recipes
claims: 65 before, 65 kept, 0 removed
$ python3 -I data/recipes/tools/quarantine.py --rights
0 recipe(s) fail the rights gate; 401 stay in the corpus
$ python3 -I data/recipes/tools/pin_rights_evidence.py --check
evidence matches git

Counts before and after, with today's gates applied to both trees. The script reads each tree's files with git show and runs the current tools; a recipe can fail several gates.

ref 4b6e163: accepted 428 in corpus.jsonl, quarantined 0 in quarantine.jsonl (none)
  claims shipped: 1050
  recipes the current gates refuse: 416
    claims: composition not proven: 400 recipe(s)
    rights: no affirmative evidence: 27 recipe(s)
    safety: FSIS endpoint: 36 recipe(s)
    safety: check not in step text: 70 recipe(s)
ref HEAD: accepted 401 in corpus.jsonl, quarantined 27 in quarantine.jsonl ({'rights': 27})
  claims shipped: 65
  recipes the current gates refuse: 0

scripts/recipe_scraper/ has no tests and this PR does not change it; python3 -I -m py_compile scripts/recipe_scraper/scrape_recipes.py exits 0, and that is the only check run on it. The repository's CI runs the iOS build and tests, which do not exercise these Python gates. Nothing here needs Xcode, and none was run.

Risk

  • Rights bar margin. 401 rights-clear recipes is one over the 400 bar. Two more quarantines drop below it.
  • Resolution bar. Quantity evidence still fails at 2612/2829 = 92.33%. The bar needs 2688, so the corpus is short 76 lines; it was short 68 at 4b6e163, and the quarantined recipes took 159 resolved lines with them. The 217 unresolved lines state no amount anywhere in their records (REPORT section 9). Closing the gap is the operator's open decision.
  • The gate is a floor. It classifies by ingredient name and reads which foods a step names. Chorizo is always treated as raw (160 °F). Bacon is a whole pork cut at 145 °F with rest, because FSIS lists no bacon row. Stuffed vegetables and traybakes are not casseroles.
  • Suite C at 9e04af4. The newer suite fails 51 recipes on its R3 rule (measured at e311653, before the NHS quarantine), and none is a missed endpoint. Its keywords classify chicken stock, canned tuna, fish sauce and burger buns as raw proteins. It also wants a check naming each class where this gate accepts a stricter check on the same food. REPORT section 10 lists the 55 violations by cause, and the validator still pins 26c7004.
  • Upstream copy. The 89 safety-step corrections live in the corpus. data/recipes/originals/ belongs to fl-original-recipes-f1, so a re-ingest from there must run fix_doneness.py until the same edits land upstream.

Handoff

  • Done. Finding 1 in fed1a05, rests moved into the check step in e311653, soft yolks in eb93e03, and chart-row classes in 976ab51. Finding 2 in 1e10b85, with affirmative readings in 7e946b2 and its test fix in 37a4e90. Finding 3 in 7733b10, with pinned evidence and the NHS quarantine in 1dfe8bb. Finding 4 in 0f39ea8, with per-claim exemptions in c3ce34d. Finding 5 in 79b36ee. REPORT in 68ad837 and 7fc68d4.
  • Not done. The 95% resolution bar is the operator's decision. The 25 seeds stay quarantined until someone produces an authorship or permission record. The 2 NHS recipes stay quarantined until someone with authority settles which NHS terms govern Healthier Families pages. Claims return only as composition data grows. The first review's lesser findings are open: NaN, null and duplicate rows in the conversions loader, the 95% bar printed but not enforced, and the substitution context checks.
  • Head: 7fc68d48e35f14cce3df1a08c37657f18b738d6a

Checklist

  • The scope is focused; related changes belong together
  • I explained what changed and why
  • N/A before/after images: data and tooling only, no visible UI change
  • N/A video: no motion or interaction change

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

Writer ruling (2026-10-11, operator). fl-original-recipes-f1 now owns PR #107, overhaul/original-recipes, and data/recipes/originals/; this programme has stopped writing that branch and directory. This programme's commits on overhaul/original-recipes — all authored Sam Gu 127461594+SamGu-NRX@users.noreply.github.com with the obvious-autobuild[bot] co-author trailer, diffs confined to data/recipes/originals/, append-only — were: Part 5 (thread th_MzvPCFVg) 654afe2, 47e9274, 9b69c34, 0b7a08b; Part 5b continuation M4-M11 (thread th_lm6J9eSh) 1855545, 0fc9b63, f484ebb, 667b759, 9bf12e5, dc3e6da, f04f508, 607ef99. Branch head at handoff: 607ef99, still this programme's last commit — the new owner has not yet appended. The operator's 19:10Z instruction to fold #111 into #107 is voided by the same ruling and was never acted on here. The food-safety rulebook (#97) belongs to fl-root-food-safety-f1. This programme's scope stands: the rights audit, PR #43, licensed ingestion into corpus.jsonl (ingesting data/recipes/originals through the same validation as any source), and substitution table (#114). Open with the operator: the 68-line quantity-gap decision (upstream re-authoring vs accept 92.74% as shipped); any data/recipes/originals/ re-authoring routes through fl-original-recipes-f1.

🤖 Generated with Claude Code

SamGu-NRX and others added 2 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>
@coderabbitai

coderabbitai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • 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 11, 2026 15:47
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>
@SamGu-NRX SamGu-NRX changed the title Rights-approved recipe corpus v2: 25 seed recipes in RS v1 (M1) Rights-approved recipe corpus v2: seeds + cleared-source ingestion (NHS, NHLBI, MyPlate) + report Oct 11, 2026
SamGu-NRX added a commit that referenced this pull request Oct 11, 2026
…s audit

Part 1b report recommendation (PR #113), step 1 of 2: land deterministic
synthetic fixtures (152 + 3 records, same record shape as the BBC scrape
caches they replace) with fictional titles, ingredient lines, step text and
example.com URLs, marked "synthetic": true on every record; a README in
.cache/ states why. The audit loader is untouched - the old caches are
deleted and the loader repointed in the follow-up commit.

Proof: make_synthetic_fixtures.py regenerates byte-identical files across
runs; generate_rights_audit.py re-run is byte-identical to the pre-change
baseline (diff empty, same sha256); pytest scripts/recipe_scraper/tests
23 passed.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
SamGu-NRX added a commit that referenced this pull request Oct 11, 2026
…fixtures

Part 1b report recommendation (PR #113), step 2 of 2: delete the four
committed BBC-carrying caches under .cache/ (bbc_goodfood_recipes.json with
152 full-text records, its 3-record .test.json, data.merged.non_destructive.json,
merge_report.json) and repoint generate_rights_audit.py's cache paths to the
synthetic fixtures landed in the previous commit. Verdict logic, thresholds,
and the per-recipe table are untouched; only path constants and the two
cache-fact bullets that name the files read changed.

Proof: audit re-run green - summary keep 25 / rewrite 141 / remove 0; diff
vs the pre-change baseline run is exactly the three cache-fact bullets
(152-with-bbcgoodfood-URLs now 0; deleted data.merged now ?); the 166-row
per-recipe table and Summary are byte-identical; pytest 23 passed; grep of
.cache shows zero bbcgoodfood occurrences in fixture data.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
SamGu-NRX and others added 2 commits October 11, 2026 18:36
…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>

@SamGu-NRX SamGu-NRX left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: BLOCK

Reviewed head 27613a1 against SPEC section 3 and task fl-recipe-platform-f1. The latest join contains 428 recipes, not the 27 still described in the Proof and REPORT.

Blockers:

  • data/recipes/corpus.jsonl:35,121,129, joined fl-original-0008, fl-original-0094 and fl-original-0102. The breakfast casserole ends at 160 F instead of the casserole requirement of 165 F; the whole-pork recipe gives 145 F without the required three-minute rest; the tuna gratin has a 145 F fish check attached to a bubbling-cheese instruction instead of a 165 F casserole check. ingest_originals.py:158-176 accepts these through the external suite. Quarantine or correct them and add controls for food class, mixed dishes and rest duration. Source: https://www.foodsafety.gov/food-safety-charts/safe-minimum-internal-temperatures
  • data/recipes/tools/convert_bundled.py:386-401. All 25 seeds receive original-work permissions because they lack a Source line, existed in an initial commit and are absent from the BBC cache. Those observations do not establish authorship or permission to redistribute. Obtain affirmative provenance or quarantine them. This is an evidence gap, not a finding that they were copied.
  • data/recipes/tools/ingest_nhs.py:671-732. Generic meat gets 160 F even if it is ground poultry. Any earlier safety annotation suppresses all fallback checks, including reheating; when no annotation exists, eggs take priority over chicken or seafood. A chicken-and-egg dish can therefore end at 160 F, and leftovers can lose their 165 F check. Check every applicable food and process, then require the strongest applicable safe endpoint and rest.
  • data/recipes/tools/convert_bundled.py:324-326 and corpus.jsonl:1,9. Safety metadata does not rewrite the instruction the cook reads. Fried rice still says to scramble eggs and stir-fry rice for 2-3 minutes; chicken rice still says simmer 18-20 minutes. Require explicit thermometer wording in the rendered instruction or a verified consumer that presents the check before completion.

Lesser findings:

  • apply_substitutions.py:113-128 labels swaps verified for any supported role without resolving the ingredient's role in this recipe. This is table association, not a recipe-context validation or an eater-allergen check. Keep that distinction in the schema and handoff.
  • validate_corpus.py:123-153 prints the quantity percentage but never fails below 95%. The original join can now bring in unknown quantities. Enforce the acceptance threshold and bind the external suite and table to exact source SHAs.
  • convert_bundled.py:291 flattens optional and required ingredients into the same bill. Preserve optionality so fit and nutrition do not claim a complete required bill incorrectly.

Criteria: rights evidence partly met; deterministic structured ingestion met; no invented mass partly met; nutrition/USDA identity missing; safe atomic steps partly met; context-specific substitutions partly met; dedupe partly met; report and stratified audit missing; scope/no workflow edits met. 2/9 met.

CI: the old head's iOS run 38154577398 passed. The exact reviewed head's run 38164707676 is queued. Those app checks do not establish the Python pipeline's safety or rights claims. No local tests, Xcode or simulator run. The latest source was inspected before reading the PR Proof; the Proof remains stale at 27 recipes.

…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>
@SamGu-NRX SamGu-NRX changed the title Rights-approved recipe corpus v2: seeds + cleared-source ingestion (NHS, NHLBI, MyPlate) + report Rights-approved recipe corpus v2: 428 rights-clear (25 seeds + 2 NHS + 401 originals) + substitution join + suite C verification Oct 11, 2026

@SamGu-NRX SamGu-NRX left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: BLOCK

Reviewed b1e6cdc for fl-recipe-platform-f1 against plan #86. This supersedes my review at 27613a1. The delta changes only REPORT.md; none of the previous source blockers was fixed. The updated counts and quantity shortfall are useful, but REPORT.md:42-48,374-386 and the Handoff incorrectly claim safety/rights clearance and only a resolution decision left.

Blockers:

  • data/recipes/corpus.jsonl:35,121,129: the breakfast casserole still specifies 160 F instead of 165 F; Citrus-Braised Pork still specifies 145 F without a three-minute rest; the tuna pasta gratin still attaches a 145 F fish check to bubbling-cheese text instead of a 165 F casserole check. tools/ingest_originals.py:158-176 accepts these through the external linter. Correct or quarantine them and require dish/process-specific checks. See the federal temperature chart.
  • data/recipes/tools/convert_bundled.py:386-401: all 25 seeds still receive original-work permissions based on no Source line, initial-commit presence and absence from a BBC cache. None establishes authorship or redistribution permission. Require affirmative provenance or quarantine. This is an evidence gap, not an allegation of copying; REPORT.md:21,42,54-57 must not call these cleared.
  • data/recipes/corpus.jsonl:92: fl-original-0065 offers “ghee or vegetable oil” while claiming dairy-free unconditionally. The pinned scanner at 26c7004, evaluation/suites/recipes/rs.py:259-285, ignores unknown ingredient names and accepts an empty conflicts list. validate_corpus.py:114-118 and ingest_originals.py:158-168 trust that result, so this ambiguous milk-based choice passes. Resolve every included ingredient and alternative; unknown composition must block restriction claims. REPORT.md:44 cannot substantiate zero unchecked claims with this scanner.
  • data/recipes/tools/ingest_nhs.py:671-732: generic meat still gets 160 F even for ground poultry; any earlier annotation suppresses fallback checks, including leftovers; eggs take priority over poultry in the fallback. Whole pork also lacks its required rest. Check every applicable food and process, including mixed dishes and reheating, before accepting a recipe.
  • data/recipes/tools/convert_bundled.py:324-326 and corpus.jsonl:1,9: attaching thermometer metadata still leaves the cook-facing instructions as “Stir-fry rice for 2-3 minutes” and “simmer 18-20 min.” Put the required measurement in the instruction, or prove a consumer presents it before completion.

Lesser findings:

  • REPORT.md:379-386: adding conversion-table references alone cannot resolve the 217 lines still marked unknown. The pinned rs.py:212-222 counts only resolved quantity kinds. Rebuild affected corpus lines from actual measure evidence after adding reviewed conversions; preserve unknown where evidence is absent. validate_corpus.py:123-153 still prints the percentage without enforcing the 95% bar.
  • tools/apply_substitutions.py:113-128 still marks all supported roles verified without identifying the ingredient's role in this recipe or checking eaters. REPORT.md:232-245 describes table association, not recipe-context or restriction clearance.
  • convert_bundled.py:291 still flattens required and optional ingredients. The expanded report still lacks the required stratified 60-recipe audit and byte-for-byte regeneration command. The corpus has no equipment, nutrition or untested fields, so the Handoff's “nothing” left is unsupported.

Criteria: rights evidence partly met; deterministic structured ingestion met; quantity evidence partly met; nutrition/USDA identity missing; safe atomic steps partly met; context-specific substitutions partly met; dedupe partly met; report/audit partly met; scope/no workflow edits met. 2/9 met.

I independently parsed 428 recipes, 2,988 ingredient lines, the 416/2,355/217 quantity-kind split, 1,050 claims and 2,087 substitution annotations on 405 recipes. CI run 38164866629 and all three checks passed at this head; the workflow/project targets cover the existing iOS app, not these Python/data gates. The PR's component-lint output does not clear the counterexamples above. No local tests, Xcode or simulator run; no exhaustive authorship or culinary clearance claimed.

SamGu-NRX and others added 2 commits October 11, 2026 20:14
…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>

@SamGu-NRX SamGu-NRX left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: BLOCK

Reviewed 1d51ce63d999fbbfe527639e9ca0e306dca8e1ae for fl-recipe-platform-f1. The delta from my reviewed b1e6cdc changes only REPORT.md. The new quantity-evidence handoff correctly assigns data fixes to this branch; it does not fix the source blockers.

Blockers:

  • data/recipes/corpus.jsonl:35,121,129: the breakfast casserole still uses 160 F; whole-cut pork still uses 145 F without the required three-minute rest; tuna pasta gratin still attaches a 145 F fish check to a visual bubbling-cheese endpoint. Correct or quarantine these. The federal temperature chart specifies 165 F for casseroles and the pork rest. The linter's green result does not establish safe dish/process checks.
  • data/recipes/tools/convert_bundled.py:386-401: the 25 seeds still receive original-work permissions from absence of a Source line, initial-commit presence and absence from a BBC cache. Those facts do not establish authorship or permission to redistribute. Require affirmative provenance or quarantine. This is an evidence gap, not an allegation of copying.
  • data/recipes/corpus.jsonl:92: fl-original-0065 still offers ghee or vegetable oil with an unconditional dairy-free claim. The pinned scanner at 26c7004, evaluation/suites/recipes/rs.py:259-285, ignores unrecognized ingredient names and accepts matching empty conflicts. validate_corpus.py:114-118 and ingest_originals.py:158-168 trust that result. Unknown composition and alternatives must block restriction claims, not establish dairy-free clearance.
  • data/recipes/tools/ingest_nhs.py:671-732: generic meat gets 160 F even for ground poultry; any earlier annotation suppresses later fallback checks, including reheating; an egg classification outranks poultry in mixed dishes. Whole pork lacks its rest. Validate every applicable food/process before accepting the recipe.
  • data/recipes/tools/convert_bundled.py:324-326, corpus.jsonl:1,9: thermometer metadata is still absent from the corresponding cook-facing rice reheating and chicken instructions. Put the check in the instruction or prove the consumer presents it before completion.

Lesser findings:

  • REPORT.md:375-382 still says nothing else blocks and reports rights, safety and claim clearance. The PR body still says only the resolution bar remains and that this task has nothing left. The corrected conversion ownership does not resolve the rights, safety and restriction failures above.
  • validate_corpus.py:123-153 prints resolution coverage but does not enforce the 95% bar. The corpus still has 217 unknown lines and needs at least 68 more evidenced resolutions. Adding conversion rows alone cannot resolve lines whose quantity kind remains unknown.
  • tools/apply_substitutions.py:113-128 still associates supported roles without identifying the role in this recipe or checking eaters. convert_bundled.py:291 still flattens optional and required ingredients. Nutrition/USDA identity, equipment, untested flags, the stratified 60-recipe audit and a byte-for-byte REPORT regeneration command remain unfinished.

Criteria, 2/9 met: rights evidence partly met; deterministic structured ingestion met; quantity evidence partly met; nutrition/USDA identity missing; safe atomic steps partly met; context-specific substitutions partly met; dedupe partly met; report/audit partly met; scope/no workflow edits met.

I independently re-parsed 428 recipes, 2,988 ingredient lines, quantity kinds 416 source_grams / 2,355 household / 217 unknown, 1,050 claims and 2,087 substitution annotations. The preceding 32fbfef passed all three checks. At this reviewed head, CodeRabbit passed and iOS build-and-test was still running when inspected. This workflow tests the existing app rather than these Python/data gates. No local tests, Xcode or simulator were run.

SamGu-NRX and others added 4 commits October 11, 2026 20:36
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>

@SamGu-NRX SamGu-NRX left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: BLOCK

Reviewed 4b6e163f4aed3e3bfa77b70197cc9322881e9135 for fl-recipe-platform-f1. Since my review at 1d51ce6, only REPORT.md and three quantity-audit/validation tools changed. The corpus, rights records and ingestion remain unchanged. Preserving unknown quantities is correct; it does not resolve the release blockers.

Blockers:

  • data/recipes/corpus.jsonl:35,121,129: the breakfast casserole uses 160 F; whole-cut pork uses 145 F without a three-minute rest; tuna pasta gratin relies on bubbling cheese with a 145 F fish annotation. Correct or quarantine these records. The federal temperature chart requires 165 F for casseroles and the pork rest.
  • data/recipes/tools/convert_bundled.py:386-401: the 25 seeds receive original-work permissions because they lack Source lines, existed in the initial commit and are absent from the BBC scrape cache. None establishes authorship or redistribution permission. Require affirmative provenance or quarantine. This is an evidence gap, not an allegation of copying.
  • data/recipes/corpus.jsonl:92: fl-original-0065 offers ghee or vegetable oil with an unconditional dairy-free claim. The pinned scanner at 26c7004, evaluation/suites/recipes/rs.py:259-285, ignores unrecognized ingredient names and accepts matching empty conflicts. data/recipes/tools/validate_corpus.py:223-228 trusts that result. Unknown composition and alternatives must prevent restriction clearance.
  • data/recipes/tools/ingest_nhs.py:671-732: the generic no-pink rewrite assigns 160 F even for ground poultry; any earlier safety annotation suppresses the later reheating check; the mixed-dish fallback chooses egg before poultry. The pork rewrite also omits rest. Check each applicable food and process, including poultry and reheating at 165 F.
  • data/recipes/tools/convert_bundled.py:324-326, data/recipes/corpus.jsonl:1,9: rice reheating and chicken checks remain metadata absent from the corresponding cooking instructions. Put them in the instruction or prove the consumer presents them before completion.

Lesser findings:

  • New data/recipes/tools/validate_corpus.py:85-100,283-291 accepts NaN/Infinity conversion masses and null name/url/quote fields because it stringifies them. Duplicate list IDs overwrite earlier rows. The downstream R2 checks at pinned rs.py:120-134 verify reference presence but never bind the row's ingredient/unit to the recipe line or verify grams = value * grams_per_unit. A valid citation for a different ingredient or measure can therefore count an unsupported mass as resolved. Reject malformed and duplicate rows, require finite values and validate each line against its conversion. No corpus conversion table ships yet; these are defects in the new acceptance path.
  • data/recipes/tools/validate_corpus.py:292-323 prints coverage without enforcing the 95% minimum. The unchanged corpus has 2,771/2,988 resolved lines, 92.74%, with 217 unknown lines and a shortfall of 68. A green exit cannot prove the release bar.
  • data/recipes/REPORT.md:464-468 and the PR body still treat rights, safety and restriction clearance as established. The narrower quantity handoff does not discharge the blockers above. The 60-recipe field audit, byte-for-byte REPORT regeneration, USDA identity/nutrition, equipment and untested flags remain unfinished. Context-specific substitution checks also remain incomplete at data/recipes/tools/apply_substitutions.py:113-128.

Criteria, 2/9 met: rights evidence partly met; deterministic structured ingestion met; quantity evidence partly met; nutrition/USDA identity missing; safe atomic steps partly met; context-specific substitutions partly met; dedupe partly met; report/audit partly met; scope/no workflow edits met.

I independently re-parsed 428 recipes and 2,988 ingredient lines, with 416 source_grams, 2,355 household and 217 unknown. All three checks passed at this exact head, including iOS build-and-test. The workflow covers the existing app, not these Python/data gates. No local tests, Xcode or simulator were run.

SamGu-NRX added a commit that referenced this pull request Oct 11, 2026
…urces

Binds each suite to the canonical sources it must consume, with the
revision verified on 2026-10-11:

- food-safety rulebook + linter: packages/FLFoodSafety + data/food-safety/
  on overhaul/food-safety-canon (PR #126, head a23439c) - consumed by
  suites C, D and E;
- allergen/diet facts: FLIngredientFacts on overhaul/ingredient-facts -
  branch pushed at head 5597db7 as a seeded skeleton (package README
  only); dataset and audit not delivered yet, recorded exactly so, with
  dependent checks failing closed until it lands;
- recipe corpus: data/recipes/REPORT.md on fleet/fl-recipe-corpus-v2
  (PR #113, head 4b6e163), which cites data/recipes/SCHEMA.md;
- accessibility identifiers: docs/screens/a11y-identifiers.md on
  overhaul/design-system (PR #96, head 2289586, fetched read-only today)
  is THE canonical contract for suites F and G; this index references it
  by path and revision and deliberately defines no identifier list of
  its own.

Binding availability rule: when a canonical source is absent from the
scored tree, dependent checks fail closed with a reason naming the
absent source - never pass, never silently skipped.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
SamGu-NRX and others added 3 commits October 11, 2026 20:43
…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>
SamGu-NRX added a commit that referenced this pull request Oct 12, 2026
Merges overhaul/food-safety-canon-p5-fixlists (7823364): nine linter-generated fix lists under docs/audits/food-safety/ (PRs #107, #111, #113, #109, #116, #102, #94, #86, #95) plus the index. Every target's content extracted verbatim into records shape, linted with fl-safety-lint at the canon head, with extraction provenance and scripts embedded; totals 813 finding-bearing findings across 2151 records; two targets scan clean. The parent thread posts the per-PR comments.

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

Copy link
Copy Markdown
Owner Author

The food-safety canon linter (fl-safety-lint, canon 299ff32, umbrella PR #126) was run over a verbatim extraction of this branch's corpus.jsonl (428 recipe records @ 4b6e163): 360 findings. Full list: docs/audits/food-safety/113-recipe-corpus-v2.md. Please apply the required changes on this branch.

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>

@SamGu-NRX SamGu-NRX left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: BLOCK

Reviewed 0f39ea8 for fl-recipe-platform-f1, including both new commits since 4b6e163. The 25 unsupported seeds are removed from the corpus and manifest into rights quarantine. Previously cited casserole/pork instructions are corrected; the ghee alternative no longer claims dairy-free. The final delta calls both text and composition gates, so the intermediate metadata-only validator finding is resolved.

Blockers:

  • data/recipes/tools/food_safety.py:220-241,302-305,365-373 treats keywords as affirmative cooking checks. With chicken ingredients and poultry/165 F metadata, an instruction saying "A thermometer reading 165 F is unnecessary; cook until no longer pink" satisfies the thermometer and temperature searches. Checked steps then skip the appearance test. Likewise, "Do not rest 3 minutes" satisfies whole-cut rest. Require an affirmative internal-food measurement and rest instruction; reject negated checks and oven temperatures. The federal chart requires food temperature measurement, not keyword presence.
  • data/recipes/tools/composition.py:71-74,132-156 removes peanut butter and almond milk from step text before every claim scan. A known lentil/oil recipe with a nut-free claim and a step-only peanut-butter serving suggestion retains that claim: no listed ingredient conflicts, and the only step evidence is erased. Exempt these phrases only from claims they actually do not contradict; resolve compound foods before checking every affected allergen. The sour-cream test does not cover this branch.
  • data/recipes/tools/food_safety.py:77-87,109 classifies venison as a 145 F whole cut. A venison roast with matching thermometer metadata and a three-minute rest therefore passes this gate below the federal chart's 160 F venison minimum. Venison is not currently in the 403 records, but this is an unsafe acceptance rule in the new corpus tooling. Use the canonical rulebook or refuse unsupported classes instead of maintaining another incomplete temperature table.
  • data/recipes/tools/rights_evidence.py:42-65 checks the spelling of an authoring-record citation or the presence of two HTTPS URLs, without resolving either to supporting evidence. A nonexistent original record with the matching id/SHA-shaped string passes; an OGL-labelled government record passes with two unrelated URLs. Verify the referenced record, recipe identity and source terms against pinned evidence before accepting. String format alone does not establish permission.

Lesser findings:

  • data/recipes/tools/validate_corpus.py:337-345 still prints quantity coverage without enforcing 95%. Static parsing at this head finds 2,618/2,835 lines with source_grams or household quantity kinds and 217 unknown. The earlier conversion-integrity and context-specific substitution findings are unchanged.
  • data/recipes/REPORT.md:464-468 and the PR's Verification/Handoff still describe 428 recipes, zero quarantine and older proof. Current files contain 403 corpus records, 25 rights-quarantined records and 65 claims, down from 981. Update the report and prove the new gates. The rights gate's 12-word overlap cutoff is a heuristic, not clearance of source rights.

Task criteria, 2/9 met:

  • Rights evidence: partly met. Seeds quarantined; evidence resolution remains missing.
  • Deterministic structured ingestion: met in the inspected tooling.
  • Quantity evidence: partly met. Unknowns preserved; coverage and conversion validation incomplete.
  • Nutrition/USDA identity: missing.
  • Safe atomic steps: partly met. Many records corrected; unsafe gate inputs remain.
  • Context-specific substitutions: partly met, unchanged.
  • Dedupe: partly met, unchanged.
  • Report/60-recipe audit: partly met. Current totals and regenerated proof missing.
  • Scope/no workflow edits: met. Own changes remain under data/recipes.

Current-head app build-and-test is in progress in run 38193861233. It does not execute these Python corpus gates; the preceding head's checks were cancelled. I inspected source, data differences and tests without running local tests. The counterexamples above are source-derived, not claimed execution results.

SamGu-NRX and others added 4 commits October 11, 2026 21:01
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>
SamGu-NRX and others added 3 commits October 11, 2026 21:41
…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>
SamGu-NRX added a commit that referenced this pull request Oct 12, 2026
…d itself

Review of PR #88 (rs.py:288-321, 532-552; suite.py:158-216):

A household line cleared whenever it carried a nonempty conversion_ref
string plus positive grams — the citation was never resolved, so an
invented reference was its own evidence. Any truthy recipe-level
provenance.scale_measurements cleared every weighed line with no linkage
between a measurement and the line it covers. And a tree carrying only
the originals product source (data/recipes/originals/, PR #107) fell
through to the bundled catalog and was scored as a failing baseline.

Now (rs.py):
- a household citation resolves to a conversion-evidence row the CORPUS
  supplies — the record's own rows (record["conversions"] or
  provenance.conversions) or the scored tree's data/recipes conversion
  file, never a harness-internal table — and that row must exist, carry a
  positive grams_per_unit, and cover this line (ingredient name, declared
  unit); carried grams must agree with the cited conversion;
- a weighed line needs a scale measurement LINKED TO THAT LINE (by
  ingredient name or line index) agreeing with its grams; a recipe-level
  flag, a bare true, or a measurement of another line clears nothing;
- honest unknown stays allowed and counts as unresolved.

(suite.py):
- data/recipes/corpus.jsonl (PR #113) and
  data/recipes/originals/originals.jsonl (PR #107) are each scored
  independently as product data with the rights evidence their own tree
  carries; originals already ingested are skipped by id (counted and
  described), the corpus-of-record manifest governs ingested refs after a
  rights-substance check, and only a tree with NEITHER product source
  falls to the bundled baseline;
- run() loads the scored tree's own conversion-evidence file
  (fail-closed on conflicts or unreadable content) and passes no
  harness-internal table.

(corpus_build.py): the composed fixture records now emit their own
conversion rows (build-time table material written onto the record), so
the fixtures model the contract; the fixture table is never a scoring
input.

Battery: suite.py --self-test catches 16/16 planted failures with the
400-recipe all-pass control and a new linked-scale control held (2/2);
new plants P6 (nonexistent ref + grams present), P7 (unrelated scale
measurement), P8 (originals-only tree scored as product data) close the
review's exact cases. selftest_c.py replays them minimally: red at base
9e04af4, 4/4 green here.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
SamGu-NRX and others added 3 commits October 11, 2026 21:44
…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>

@SamGu-NRX SamGu-NRX left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: BLOCK

Reviewed 7fc68d4 for fl-recipe-platform-f1. The new gate, ingestion and test changes were inspected, along with all changed corpus steps. The four prior examples are addressed: negated checks now refuse, peanut butter survives the nut-free scan, venison refuses, and rights citations resolve against pinned evidence. Static comparison confirms all 401 pinned original records match their source manifest/title/ingredient-name hash at 607ef99. The corpus now contains 401 originals and 27 quarantines. Quarantining the two NHS records agrees with the Healthier Families terms, which require permission for commercial content use.

Blockers:

  • data/recipes/tools/food_safety.py:305-316,384-388,475-481 still does not interpret the temperature comparison. Chicken plus poultry/165 metadata and the step "Cook the chicken until a food thermometer in its centre reads below 165 °F" satisfies affirmative_check: thermometer, reads and 165 are present, with neither recognized negation nor oven setting. text_temps sees only 165, so no lower numeric temperature is rejected. Require a reading at or above the minimum, and reject below/less-than instructions. The federal chart specifies a minimum internal temperature. This is a source-derived acceptance path, not an executed test.
  • data/recipes/tools/composition.py:139-150,168-180 still resolves only listed ingredients, then uses finite word lists for foods introduced in steps. A known lentil/oil recipe claiming dairy-free with "Fold in mascarpone before serving" has no listed conflict and no step-pattern match: mascarpone appears in neither STEP_CONFLICTS nor STEP_COMPOUNDS. Unlisted step ingredients need composition evidence too. Refuse or remove claims until those foods are reconciled with the ingredient list and canonical facts. Adding pesto closes that example but does not make unknown step composition fail closed.

Lesser findings:

  • data/recipes/tools/ingest_nhs.py:725-727,755-760 can put UNCHARTED into _endpoint, which indexes MIN_F without that key. An uncharted meat with a matching doneness clause crashes ingestion instead of returning a refused page with a reason.
  • data/recipes/tools/validate_corpus.py still prints the 95% quantity bar without enforcing it. Static counts are 2,612 source_grams/household lines out of 2,829, with 217 unknown, matching the updated report's 92.33%. Earlier conversion-integrity and context-specific substitution findings remain.
  • REPORT.md now gives current totals and limits, but the PR Verification/Handoff still claims 428 rights-clear recipes, zero quarantine and older proof. Update the body and prove these gates at this head. The overlap cutoff remains a heuristic, not evidence of permission.

Task criteria, 3/9 met:

  • Rights evidence: met for the shipped corpus. Unsupported seeds/NHS are quarantined; original records resolve to pinned source evidence.
  • Deterministic structured ingestion: met, with the uncharted-input error-handling issue above.
  • Quantity evidence: partly met. Unknowns remain explicit; coverage and conversion validation remain incomplete.
  • Nutrition/USDA identity: missing, unchanged.
  • Safe atomic steps: partly met. Corrected corpus instructions and stronger tests; unsafe acceptance paths remain.
  • Context-specific substitutions: partly met, unchanged.
  • Dedupe: partly met, unchanged.
  • Report/60-recipe audit: partly met. Current report improved; PR proof and the remaining acceptance bars are unfinished.
  • Scope/no workflow edits: met. Delta stays under data/recipes.

Current-head build-and-test was queued in run 38197238709 at inspection. The app workflow does not execute these Python gates. New tests cover the repaired examples and pinned evidence reproduction; no local tests ran.

@SamGu-NRX SamGu-NRX changed the title Rights-approved recipe corpus v2: 428 rights-clear (25 seeds + 2 NHS + 401 originals) + substitution join + suite C verification Rights-approved recipe corpus: 401 originals behind FSIS safety, rights and claim gates Oct 12, 2026
@SamGu-NRX
SamGu-NRX merged commit ecd5592 into alpha Oct 16, 2026
3 checks passed
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.

1 participant