FAILURE MAP
← Case archive

FA-97201 / Recipe scaling and nutrition / Open access

Roast timing and carryover planner: rest time clamp · case 01

A 7 kg roast rests 35 minutes.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The rest time has no 30 minute cap.

VERIFIED REPAIR

Clamp rest time to 10-30 minutes.

Unsuccessful approach: Keeping the cap but dropping the 10 minute floor rests small roasts for 5 minutes.

Case contract

Doneness (trimmed, lower) rare 52C/30 min per kg, medium rare 57/35, medium 63/40, well 71/50; unknown -> error; weight <= 0 -> error. Carryover 2C below 1.5 kg, 4C below 3 kg, else 6C. Beef and lamb pull at target - carryover. Poultry ignores doneness: pull at 74 (no carryover credit) and uses the well-done rate. cook_min = ceiling to a multiple of 5 of weight*rate. rest_min = half-up weight*5 clamped to [10, 30].

Why this case matters

Roast planners must account for carryover cooking and food-safety minimums for poultry.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(weight_kg, doneness, cut):
    T = {'rare': 52, 'medium rare': 57, 'medium': 63, 'well': 71}
    PER = {'rare': 30, 'medium rare': 35, 'medium': 40, 'well': 50}
    d = doneness.strip().lower()
    if d not in T:
        return 'error: doneness'
    if weight_kg <= 0:
        return 'error: weight'
    w = Fraction(str(weight_kg))
    carry = 2 if w < Fraction(3, 2) else 4 if w < 3 else 6
    if cut == 'poultry':
        pull = 74
    else:
        pull = T[d] - carry
    per = PER['well'] if cut == 'poultry' else PER[d]
    cook = 5 * math.ceil(w * per / 5)
    rest = max(10, math.floor(w * 5 + Fraction(1, 2)))
    return {'pull_c': pull, 'cook_min': cook, 'rest_min': rest}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['regression: rest time clamp', [7, 'well', 'poultry'], {'cook_min': 350, 'pull_c': 74, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'rare', 'lamb'], {'cook_min': 45, 'pull_c': 48, 'rest_min': 10}],
  ['generated control 1', [3.5, 'medium rare', 'poultry'], {'cook_min': 175, 'pull_c': 74, 'rest_min': 18}],
  ['generated control 2', [6, 'blue', 'beef'], 'error: doneness'],
  ['generated control 3', [2.4, 'Medium', 'lamb'], {'cook_min': 100, 'pull_c': 59, 'rest_min': 12}]],
 [['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['regression: rest time clamp', [7, 'well', 'beef'], {'cook_min': 350, 'pull_c': 65, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'rare', 'lamb'], {'cook_min': 45, 'pull_c': 48, 'rest_min': 10}],
  ['generated control 1', [2.4, 'rare', 'lamb'], {'cook_min': 75, 'pull_c': 48, 'rest_min': 12}],
  ['generated control 2', [6, 'blue', 'lamb'], 'error: doneness'],
  ['generated control 3', [1.2, 'Medium', 'beef'], {'cook_min': 50, 'pull_c': 61, 'rest_min': 10}]],
 [['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['regression: rest time clamp', [7, 'Medium', 'beef'], {'cook_min': 280, 'pull_c': 57, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'Medium', 'beef'], {'cook_min': 60, 'pull_c': 59, 'rest_min': 10}],
  ['generated control 1', [2, 'medium rare', 'lamb'], {'cook_min': 70, 'pull_c': 53, 'rest_min': 10}],
  ['generated control 2', [1, 'medium rare', 'lamb'], {'cook_min': 35, 'pull_c': 55, 'rest_min': 10}],
  ['generated control 3', [4.2, 'blue', 'beef'], 'error: doneness']],
 [['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['regression: rest time clamp', [7, 'rare', 'beef'], {'cook_min': 210, 'pull_c': 46, 'rest_min': 30}],
  ['repair check: rest time clamp', [0.5, 'Medium', 'beef'], {'cook_min': 20, 'pull_c': 61, 'rest_min': 10}],
  ['generated control 1', [2.4, 'well', 'beef'], {'cook_min': 120, 'pull_c': 67, 'rest_min': 12}],
  ['generated control 2', [6, 'Medium', 'beef'], {'cook_min': 240, 'pull_c': 57, 'rest_min': 30}],
  ['generated control 3', [2, 'medium rare', 'beef'], {'cook_min': 70, 'pull_c': 53, 'rest_min': 10}]],
 [['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['regression: rest time clamp', [7, 'well', 'poultry'], {'cook_min': 350, 'pull_c': 74, 'rest_min': 30}],
  ['repair check: rest time clamp', [0.5, 'well', 'beef'], {'cook_min': 25, 'pull_c': 69, 'rest_min': 10}],
  ['generated control 1', [1.2, 'blue', 'lamb'], 'error: doneness'],
  ['generated control 2', [0.5, 'blue', 'beef'], 'error: doneness'],
  ['generated control 3', [6, 'blue', 'beef'], 'error: doneness']]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
small rare roast{'cook_min': 30, 'pull_c': 50, 'rest_min': 10}{'cook_min': 30, 'pull_c': 50, 'rest_min': 10}Passed
large medium roast{'cook_min': 170, 'pull_c': 57, 'rest_min': 21}{'cook_min': 170, 'pull_c': 57, 'rest_min': 21}Passed
chicken{'cook_min': 100, 'pull_c': 74, 'rest_min': 10}{'cook_min': 100, 'pull_c': 74, 'rest_min': 10}Passed
regression: rest time clamp{'cook_min': 350, 'pull_c': 74, 'rest_min': 35}{'cook_min': 350, 'pull_c': 74, 'rest_min': 30}Failed
repair check: rest time clamp{'cook_min': 45, 'pull_c': 48, 'rest_min': 10}{'cook_min': 45, 'pull_c': 48, 'rest_min': 10}Passed
generated control 1{'cook_min': 175, 'pull_c': 74, 'rest_min': 18}{'cook_min': 175, 'pull_c': 74, 'rest_min': 18}Passed
generated control 2error: donenesserror: donenessPassed
generated control 3{'cook_min': 100, 'pull_c': 59, 'rest_min': 12}{'cook_min': 100, 'pull_c': 59, 'rest_min': 12}Passed

SHA-256 / eb648cd17cb7c32684493f7cd38d222fb15e096bd02b8d8da207f030f07b7c3f

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(weight_kg, doneness, cut):
    T = {'rare': 52, 'medium rare': 57, 'medium': 63, 'well': 71}
    PER = {'rare': 30, 'medium rare': 35, 'medium': 40, 'well': 50}
    d = doneness.strip().lower()
    if d not in T:
        return 'error: doneness'
    if weight_kg <= 0:
        return 'error: weight'
    w = Fraction(str(weight_kg))
    carry = 2 if w < Fraction(3, 2) else 4 if w < 3 else 6
    if cut == 'poultry':
        pull = 74
    else:
        pull = T[d] - carry
    per = PER['well'] if cut == 'poultry' else PER[d]
    cook = 5 * math.ceil(w * per / 5)
    rest = min(30, math.floor(w * 5 + Fraction(1, 2)))
    return {'pull_c': pull, 'cook_min': cook, 'rest_min': rest}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['regression: rest time clamp', [7, 'well', 'poultry'], {'cook_min': 350, 'pull_c': 74, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'rare', 'lamb'], {'cook_min': 45, 'pull_c': 48, 'rest_min': 10}],
  ['generated control 1', [3.5, 'medium rare', 'poultry'], {'cook_min': 175, 'pull_c': 74, 'rest_min': 18}],
  ['generated control 2', [6, 'blue', 'beef'], 'error: doneness'],
  ['generated control 3', [2.4, 'Medium', 'lamb'], {'cook_min': 100, 'pull_c': 59, 'rest_min': 12}]],
 [['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['regression: rest time clamp', [7, 'well', 'beef'], {'cook_min': 350, 'pull_c': 65, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'rare', 'lamb'], {'cook_min': 45, 'pull_c': 48, 'rest_min': 10}],
  ['generated control 1', [2.4, 'rare', 'lamb'], {'cook_min': 75, 'pull_c': 48, 'rest_min': 12}],
  ['generated control 2', [6, 'blue', 'lamb'], 'error: doneness'],
  ['generated control 3', [1.2, 'Medium', 'beef'], {'cook_min': 50, 'pull_c': 61, 'rest_min': 10}]],
 [['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['regression: rest time clamp', [7, 'Medium', 'beef'], {'cook_min': 280, 'pull_c': 57, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'Medium', 'beef'], {'cook_min': 60, 'pull_c': 59, 'rest_min': 10}],
  ['generated control 1', [2, 'medium rare', 'lamb'], {'cook_min': 70, 'pull_c': 53, 'rest_min': 10}],
  ['generated control 2', [1, 'medium rare', 'lamb'], {'cook_min': 35, 'pull_c': 55, 'rest_min': 10}],
  ['generated control 3', [4.2, 'blue', 'beef'], 'error: doneness']],
 [['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['regression: rest time clamp', [7, 'rare', 'beef'], {'cook_min': 210, 'pull_c': 46, 'rest_min': 30}],
  ['repair check: rest time clamp', [0.5, 'Medium', 'beef'], {'cook_min': 20, 'pull_c': 61, 'rest_min': 10}],
  ['generated control 1', [2.4, 'well', 'beef'], {'cook_min': 120, 'pull_c': 67, 'rest_min': 12}],
  ['generated control 2', [6, 'Medium', 'beef'], {'cook_min': 240, 'pull_c': 57, 'rest_min': 30}],
  ['generated control 3', [2, 'medium rare', 'beef'], {'cook_min': 70, 'pull_c': 53, 'rest_min': 10}]],
 [['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['regression: rest time clamp', [7, 'well', 'poultry'], {'cook_min': 350, 'pull_c': 74, 'rest_min': 30}],
  ['repair check: rest time clamp', [0.5, 'well', 'beef'], {'cook_min': 25, 'pull_c': 69, 'rest_min': 10}],
  ['generated control 1', [1.2, 'blue', 'lamb'], 'error: doneness'],
  ['generated control 2', [0.5, 'blue', 'beef'], 'error: doneness'],
  ['generated control 3', [6, 'blue', 'beef'], 'error: doneness']]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
small rare roast{'cook_min': 30, 'pull_c': 50, 'rest_min': 5}{'cook_min': 30, 'pull_c': 50, 'rest_min': 10}Failed
large medium roast{'cook_min': 170, 'pull_c': 57, 'rest_min': 21}{'cook_min': 170, 'pull_c': 57, 'rest_min': 21}Passed
chicken{'cook_min': 100, 'pull_c': 74, 'rest_min': 10}{'cook_min': 100, 'pull_c': 74, 'rest_min': 10}Passed
regression: rest time clamp{'cook_min': 350, 'pull_c': 74, 'rest_min': 30}{'cook_min': 350, 'pull_c': 74, 'rest_min': 30}Passed
repair check: rest time clamp{'cook_min': 45, 'pull_c': 48, 'rest_min': 8}{'cook_min': 45, 'pull_c': 48, 'rest_min': 10}Failed
generated control 1{'cook_min': 175, 'pull_c': 74, 'rest_min': 18}{'cook_min': 175, 'pull_c': 74, 'rest_min': 18}Passed
generated control 2error: donenesserror: donenessPassed
generated control 3{'cook_min': 100, 'pull_c': 59, 'rest_min': 12}{'cook_min': 100, 'pull_c': 59, 'rest_min': 12}Passed

SHA-256 / 25e36f711b7a761d7f2d199be17579d90ca0287a59ab30ab5a2dcc9e02ce93af

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(weight_kg, doneness, cut):
    T = {'rare': 52, 'medium rare': 57, 'medium': 63, 'well': 71}
    PER = {'rare': 30, 'medium rare': 35, 'medium': 40, 'well': 50}
    d = doneness.strip().lower()
    if d not in T:
        return 'error: doneness'
    if weight_kg <= 0:
        return 'error: weight'
    w = Fraction(str(weight_kg))
    carry = 2 if w < Fraction(3, 2) else 4 if w < 3 else 6
    if cut == 'poultry':
        pull = 74
    else:
        pull = T[d] - carry
    per = PER['well'] if cut == 'poultry' else PER[d]
    cook = 5 * math.ceil(w * per / 5)
    rest = min(30, max(10, math.floor(w * 5 + Fraction(1, 2))))
    return {'pull_c': pull, 'cook_min': cook, 'rest_min': rest}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['regression: rest time clamp', [7, 'well', 'poultry'], {'cook_min': 350, 'pull_c': 74, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'rare', 'lamb'], {'cook_min': 45, 'pull_c': 48, 'rest_min': 10}],
  ['generated control 1', [3.5, 'medium rare', 'poultry'], {'cook_min': 175, 'pull_c': 74, 'rest_min': 18}],
  ['generated control 2', [6, 'blue', 'beef'], 'error: doneness'],
  ['generated control 3', [2.4, 'Medium', 'lamb'], {'cook_min': 100, 'pull_c': 59, 'rest_min': 12}]],
 [['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['regression: rest time clamp', [7, 'well', 'beef'], {'cook_min': 350, 'pull_c': 65, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'rare', 'lamb'], {'cook_min': 45, 'pull_c': 48, 'rest_min': 10}],
  ['generated control 1', [2.4, 'rare', 'lamb'], {'cook_min': 75, 'pull_c': 48, 'rest_min': 12}],
  ['generated control 2', [6, 'blue', 'lamb'], 'error: doneness'],
  ['generated control 3', [1.2, 'Medium', 'beef'], {'cook_min': 50, 'pull_c': 61, 'rest_min': 10}]],
 [['chicken', [2, 'medium', 'poultry'], {'cook_min': 100, 'pull_c': 74, 'rest_min': 10}],
  ['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['regression: rest time clamp', [7, 'Medium', 'beef'], {'cook_min': 280, 'pull_c': 57, 'rest_min': 30}],
  ['repair check: rest time clamp', [1.5, 'Medium', 'beef'], {'cook_min': 60, 'pull_c': 59, 'rest_min': 10}],
  ['generated control 1', [2, 'medium rare', 'lamb'], {'cook_min': 70, 'pull_c': 53, 'rest_min': 10}],
  ['generated control 2', [1, 'medium rare', 'lamb'], {'cook_min': 35, 'pull_c': 55, 'rest_min': 10}],
  ['generated control 3', [4.2, 'blue', 'beef'], 'error: doneness']],
 [['bad doneness', [2, 'blue', 'beef'], 'error: doneness'],
  ['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['regression: rest time clamp', [7, 'rare', 'beef'], {'cook_min': 210, 'pull_c': 46, 'rest_min': 30}],
  ['repair check: rest time clamp', [0.5, 'Medium', 'beef'], {'cook_min': 20, 'pull_c': 61, 'rest_min': 10}],
  ['generated control 1', [2.4, 'well', 'beef'], {'cook_min': 120, 'pull_c': 67, 'rest_min': 12}],
  ['generated control 2', [6, 'Medium', 'beef'], {'cook_min': 240, 'pull_c': 57, 'rest_min': 30}],
  ['generated control 3', [2, 'medium rare', 'beef'], {'cook_min': 70, 'pull_c': 53, 'rest_min': 10}]],
 [['zero weight', [0, 'rare', 'beef'], 'error: weight'],
  ['small rare roast', [1, 'rare', 'beef'], {'cook_min': 30, 'pull_c': 50, 'rest_min': 10}],
  ['large medium roast', [4.2, 'medium', 'beef'], {'cook_min': 170, 'pull_c': 57, 'rest_min': 21}],
  ['regression: rest time clamp', [7, 'well', 'poultry'], {'cook_min': 350, 'pull_c': 74, 'rest_min': 30}],
  ['repair check: rest time clamp', [0.5, 'well', 'beef'], {'cook_min': 25, 'pull_c': 69, 'rest_min': 10}],
  ['generated control 1', [1.2, 'blue', 'lamb'], 'error: doneness'],
  ['generated control 2', [0.5, 'blue', 'beef'], 'error: doneness'],
  ['generated control 3', [6, 'blue', 'beef'], 'error: doneness']]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
small rare roast{'cook_min': 30, 'pull_c': 50, 'rest_min': 10}{'cook_min': 30, 'pull_c': 50, 'rest_min': 10}Passed
large medium roast{'cook_min': 170, 'pull_c': 57, 'rest_min': 21}{'cook_min': 170, 'pull_c': 57, 'rest_min': 21}Passed
chicken{'cook_min': 100, 'pull_c': 74, 'rest_min': 10}{'cook_min': 100, 'pull_c': 74, 'rest_min': 10}Passed
regression: rest time clamp{'cook_min': 350, 'pull_c': 74, 'rest_min': 30}{'cook_min': 350, 'pull_c': 74, 'rest_min': 30}Passed
repair check: rest time clamp{'cook_min': 45, 'pull_c': 48, 'rest_min': 10}{'cook_min': 45, 'pull_c': 48, 'rest_min': 10}Passed
generated control 1{'cook_min': 175, 'pull_c': 74, 'rest_min': 18}{'cook_min': 175, 'pull_c': 74, 'rest_min': 18}Passed
generated control 2error: donenesserror: donenessPassed
generated control 3{'cook_min': 100, 'pull_c': 59, 'rest_min': 12}{'cook_min': 100, 'pull_c': 59, 'rest_min': 12}Passed

SHA-256 / 9f5bf21f131a30a33aebb7e4ff229682c8573837de1661cef96dea3f2d925956

Verification & scope

A deterministic toy contract stated in full here; it is a bounded teaching model, not an authoritative reference or standards implementation. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:52:29.834529+00:00.

Case digest / 7c4b4f8e67812c3baaaf4b02c67af3f7dfca274fdbea68e142049a85d2f16f29