Skip to content

fix(evaluation): bind the stated rest to the text the cook reads - #138

Merged
SamGu-NRX merged 2 commits into
eval/scorecard-runnerfrom
eval/scorecard-fix-rest-metadata
Oct 12, 2026
Merged

SamGu-NRX merged 2 commits into
eval/scorecard-runnerfrom
eval/scorecard-fix-rest-metadata

Conversation

@SamGu-NRX

Copy link
Copy Markdown
Owner

Why

Second reviewer-confirmed defect from the PR #88 review (reproduced at 393b5de by calling rs.py directly, still live on eval/scorecard-runner at 71b5518): in evaluation/suites/recipes/rs.py, _stated_rest returned the check metadata's rest_minutes before ever reading the step text. The cook reads the instruction text, not the metadata, so:

  • metadata rest_minutes: 3 with text "Rest 1 minute" returned 3.0 — the whole-cut check cleared on a rest the instructions never state;
  • metadata rest_minutes: 3 with no rest stated in the text also returned 3.0 — the cook was never told to rest the food, and the required-rest check passed anyway.

What changed

The stated rest is now bound to the text the cook reads — the same cook-visibility principle fix 1 (71b5518) applied to the internal reading:

  • _stated_rest(step) reads only the step text (_REST_TEXT): "Rest 1 minute" → 1.0; no stated rest → None. The metadata never substitutes.
  • When the text has no rest where the class requires one, the required-rest violation fires (recipe fails).
  • When the check metadata also names a rest that DISAGREES with the text, the linter adds a violation naming both values — neither value is silently preferred; when they agree (or the metadata is silent) nothing changes.

Boundary: a rest_minutes value agreeing with the text passes unchanged; a check with no rest_minutes metadata and a valid text rest passes unchanged (the 400-recipe control corpus pattern).

Red/green proof

Both reviewer cases are planted as D-series plants D1f/D1g in the suite C planted-failure battery.

Red (844bf1c, rs.py untouched — the defect is live):

stated = 3.0
stated = 3.0
plant D1f whole-cut rest clears on rest_minutes metadata over the text's 1-minute rest: MISSED (food_safety_linter_pass=pass)
plant D1g whole-cut rest clears on rest_minutes metadata with no rest in the text: MISSED (food_safety_linter_pass=pass)
caught 17/19 planted failures; controls 2/2 held        (suite.py --self-test, exit 1)
run.py --self-test: SELF-TEST FAILED 35/36              (exit 2)

Green (2eedb0a, the fix):

stated = 1.0
stated = None
plant D1f ...: caught (food_safety_linter_pass=fail)   <- below-minimum + disagreement naming both values
plant D1g ...: caught (food_safety_linter_pass=fail)   <- must state the required 3-minute rest
caught 19/19 planted failures; controls 2/2 held; part-A safety-lint battery: 4/4 plants, controls held   (suite.py --self-test, exit 0)
self-test ok: 36/36 runner cases + suites behaved as required   (run.py --self-test, exit 0)
all 8 plants caught: the runner refused every forgery            (planted_failures.py --expect-green, exit 0)
allergen-facts verdict self-tests: 7 passed, 0 failed, 0 skipped (test_allergen_facts.py, exit 0)
selftest_c: 4/4 match the fixed contract                         (selftest_c.py, exit 0)

Exact outputs: evaluation/tests/evidence-red-rest-metadata.txt and evaluation/tests/evidence-green-rest-metadata.txt.

No currently-passing control regressed: D1c (whole-cut without the required 3-minute rest) stays caught; the part-A controls (whole-cut text 145F + 3-min rest; poultry text 165F) and the 400-recipe clean corpus stay clean.

Tradeoffs

  • The stated rest now comes from the text only. A recipe whose text states a valid rest (>= required) but carries a disagreeing rest_minutes metadata now FAILS until the two agree. That is deliberate: the fix bar is "never silently prefer either" — a metadata value the cook never sees must not quietly bless a different rest, even a safe one.
  • _stated_rest lost its chk parameter: with metadata no longer feeding the stated rest, keeping the parameter would misstate the function's inputs. The reviewer's direct-call repro rs._stated_rest(step, chk) becomes rs._stated_rest(step).
  • The disagreement flag requires both values to be present; metadata claiming a rest while the text states none is reported by the existing must-state violation (naming the requirement), not a second disagreement line.

Handoff

Done:

  • D1f/D1g planted red (844bf1c), fix landed (2eedb0a), full battery green at branch head 2eedb0a; evidence committed under evaluation/tests/.
  • New counts: suite C battery 19/19 plants + 2/2 controls + part-A battery 4/4 plants and controls held; runner 36/36; runner planted failures 8/8; allergen-facts 7/7; selftest_c 4/4 — every command exit 0.

Left:

How to continue:

  • Re-verify at the branch head with the five battery commands (all exit 0): python3 evaluation/run.py --self-test; python3 evaluation/tests/planted_failures.py --expect-green; python3 evaluation/suites/recipes/suite.py --self-test; python3 evaluation/suites/recipes/test_allergen_facts.py; python3 evaluation/suites/recipes/selftest_c.py.
  • The branch's commits are additive on 71b5518; if eval/scorecard-runner moves before the join, re-run the battery at the merge head (never rewrite this branch's history).

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

SamGu-NRX and others added 2 commits October 12, 2026 04:03
Reviewer-reproduced at 393b5de by calling rs.py directly:
rs._stated_rest(step, chk) returns the check metadata's rest_minutes
before ever reading the step text — metadata rest_minutes: 3 with text
'Rest 1 minute' returns 3.0, and metadata rest_minutes: 3 with no rest
stated in the text also returns 3.0. The cook reads the text, not the
metadata, so a whole-cut check cleared on a 1-minute stated rest (or on
no stated rest at all) whenever the metadata claimed 3.

The reviewer's two cases become plants D1f and D1g in the suite C
planted-failure battery. Before the fix the linter accepts the
metadata's 3.0 as the stated rest and both plants are MISSED — the
battery runs red by design:

    stated = 3.0
    stated = 3.0
    plant D1f whole-cut rest clears on rest_minutes metadata over the
    text's 1-minute rest: MISSED (food_safety_linter_pass=pass)
    plant D1g whole-cut rest clears on rest_minutes metadata with no
    rest in the text: MISSED (food_safety_linter_pass=pass)
    caught 17/19 planted failures; controls 2/2 held (suite C, exit 1)
    run.py --self-test: SELF-TEST FAILED 35/36  (exit 2)

Evidence: evaluation/tests/evidence-red-rest-metadata.txt. The fix lands
in the next commit; rs.py is untouched here.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
_stated_rest returned the check metadata's rest_minutes before ever
reading the step text, so metadata rest_minutes: 3 cleared a whole-cut
check when the instruction text stated 'Rest 1 minute' (the cook reads
1, the check credited 3) and when the text stated no rest at all
(reviewer-reproduced at 393b5de by calling rs.py directly).

The stated rest is now what the instruction text says (_REST_TEXT on
the step text only — the same cook-visibility principle as the internal
reading): 'Rest 1 minute' yields 1.0, no stated rest yields None and
fails the required-rest check. When the check metadata ALSO names a
rest that disagrees with the text, the linter flags the disagreement
naming both values — neither is silently preferred; when they agree
the recipe passes unchanged.

The reviewer's two cases are planted as battery cases D1f/D1g
(previous commit). Before the fix the metadata's 3.0 was returned and
both plants were MISSED; now the same plants are caught and the full
battery is green at this head:

    stated = 1.0
    stated = None
    caught 19/19 planted failures; controls 2/2 held;
    part-A safety-lint battery: 4/4 plants, controls held   (suite C, exit 0)
    self-test ok: 36/36 runner cases + suites behaved as required (exit 0)
    all 8 plants caught: the runner refused every forgery   (exit 0)
    allergen-facts verdict self-tests: 7 passed, 0 failed   (exit 0)
    selftest_c: 4/4 match the fixed contract                 (exit 0)

Valid patterns still yield their own values (text 'rest ... 3 minutes'
-> 3.0, 'rest the meat 5 minutes' -> 5.0), and metadata agreeing with
the text passes with no flag. No passing control regressed: D1c stays
caught, the part-A controls (whole-cut text 145F + 3-min rest; poultry
text 165F) and the 400-recipe clean corpus stay clean.

Evidence: evaluation/tests/evidence-green-rest-metadata.txt (red:
evaluation/tests/evidence-red-rest-metadata.txt).

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

coderabbitai Bot commented Oct 12, 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
SamGu-NRX merged commit 2eedb0a into eval/scorecard-runner Oct 12, 2026
2 checks passed
SamGu-NRX added a commit that referenced this pull request Oct 12, 2026
The committed evidence at 393b5de was produced by the pre-fix rs.py;
regenerated per SCORECARD.md from a completely clean tree at 2eedb0a
(the r3 join plus the two reviewer-finding fixes: oven-reading #137
and rest-metadata #138, landed red-first as plants D1e/D1f/D1g,
each reviewer-reproduced at 393b5de by calling rs.py directly).

Rendered state unchanged: totals 1 pass / 4 fail / 9 not-run; suite A
re-measured identically (37 stock double-counts, 209 unevidenced
sufficiency claims, 55/200 fit-line, 3/3 battery rows) producer-bound;
suite C still quarantined 0/166 with downstream metrics not-run
fail-closed; B/D/E not-run naming owners #115/#101/#104; F/G not-run
on the macos-import rule. BASELINE.md records the new run
(r-20261012T041423Z-716a5d80) with the new battery counts (recipes
19/19 plants + 2/2 controls + part-A 4/4).

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
@SamGu-NRX
SamGu-NRX deleted the eval/scorecard-fix-rest-metadata branch October 15, 2026 11:30
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