FAILURE MAP
← Case archive

FA-97741 / Knitting and sewing pattern grading / Open access

Knitting needle size converter: tie-break toward larger · case 01

5.75 mm converts to US 9 instead of US 10.

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

ROOT CAUSE

Equidistant sizes keep the first (smaller) match.

VERIFIED REPAIR

Break ties toward the larger millimetre size.

Unsuccessful approach: Comparing US labels as strings ranks "9" above "10".

Case contract

Table US->mm: 0 2.0, 1 2.25, 2 2.75, 3 3.25, 4 3.5, 5 3.75, 6 4.0, 7 4.5, 8 5.0, 9 5.5, 10 6.0, 10.5 6.5, 11 8.0, 13 9.0, 15 10.0. "us->mm": trim, "10 1/2" means "10.5", exact match or "error: size". "mm->us": nearest size within 0.5 mm inclusive, ties to the larger mm, else "error: size". Other directions -> "error: direction".

Why this case matters

Patterns list needle sizes in US or metric and knitters convert between them.

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(value, direction):
    T = [('0', 2.0), ('1', 2.25), ('2', 2.75), ('3', 3.25), ('4', 3.5), ('5', 3.75), ('6', 4.0), ('7', 4.5),
         ('8', 5.0), ('9', 5.5), ('10', 6.0), ('10.5', 6.5), ('11', 8.0), ('13', 9.0), ('15', 10.0)]
    if direction == 'us->mm':
        v = str(value).strip().replace(' 1/2', '.5')
        for us, mm in T:
            if us == v:
                return mm
        return 'error: size'
    if direction == 'mm->us':
        x = Fraction(str(value))
        best = None
        for us, mm in T:
            d = abs(Fraction(str(mm)) - x)
            if d > Fraction(1, 2):
                continue
            if best is None or d < best[0] or False:
                best = (d, us, mm)
        return best[1] if best else 'error: size'
    return 'error: direction' 
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['US 8', ['8', 'us->mm'], 5.0], ['half size', ['10 1/2', 'us->mm'], 6.5],
  ['tie between 9 and 10', [5.75, 'mm->us'], '10'],
  ['regression: tie-break toward larger', [6.25, 'mm->us'], '10.5'],
  ['generated control 1', [11, 'us->mm'], 8.0], ['generated control 2', [3.3, 'uk'], 'error: direction'],
  ['generated control 3', ['10.5', 'us->mm'], 6.5],
  ['generated control 4', [1.5, 'uk'], 'error: direction']],
 [['half size', ['10 1/2', 'us->mm'], 6.5], ['tie between 9 and 10', [5.75, 'mm->us'], '10'],
  ['edge of tolerance', [1.5, 'mm->us'], '0'],
  ['regression: tie-break toward larger', [3.625, 'mm->us'], '5'],
  ['generated control 1', ['0', 'us->mm'], 2.0], ['generated control 2', [3.0, 'mm->us'], '3'],
  ['generated control 3', [' 7 ', 'us->mm'], 4.5], ['generated control 4', ['8', 'us->mm'], 5.0]],
 [['tie between 9 and 10', [5.75, 'mm->us'], '10'], ['edge of tolerance', [1.5, 'mm->us'], '0'],
  ['no match', [11.0, 'mm->us'], 'error: size'],
  ['regression: tie-break toward larger', [6.25, 'mm->us'], '10.5'],
  ['generated control 1', [7.0, 'mm->us'], '10.5'], ['generated control 2', ['10.5', 'us->mm'], 6.5],
  ['generated control 3', [7.25, 'mm->us'], 'error: size'],
  ['generated control 4', ['12', 'us->mm'], 'error: size']],
 [['edge of tolerance', [1.5, 'mm->us'], '0'], ['no match', [11.0, 'mm->us'], 'error: size'],
  ['US 8', ['8', 'us->mm'], 5.0], ['regression: tie-break toward larger', [4.25, 'mm->us'], '7'],
  ['repair check: tie-break toward larger', [5.75, 'mm->us'], '10'],
  ['generated control 1', [7.0, 'mm->us'], '10.5'], ['generated control 2', ['10.5', 'us->mm'], 6.5],
  ['generated control 3', [1.5, 'uk'], 'error: direction']],
 [['no match', [11.0, 'mm->us'], 'error: size'], ['US 8', ['8', 'us->mm'], 5.0],
  ['half size', ['10 1/2', 'us->mm'], 6.5], ['regression: tie-break toward larger', [3.625, 'mm->us'], '5'],
  ['repair check: tie-break toward larger', [5.75, 'mm->us'], '10'],
  ['generated control 1', ['10.5', 'us->mm'], 6.5], ['generated control 2', ['12', 'us->mm'], 'error: size'],
  ['generated control 3', [3.3, 'mm->us'], '3']]]
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
US 85.05.0Passed
half size6.56.5Passed
tie between 9 and 10910Failed
regression: tie-break toward larger1010.5Failed
generated control 18.08.0Passed
generated control 2error: directionerror: directionPassed
generated control 36.56.5Passed
generated control 4error: directionerror: directionPassed

SHA-256 / a6fccb6bcc5088459d8ed06ffbedc0a320d37e3e2ecd8f0d3cc274b91e057885

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(value, direction):
    T = [('0', 2.0), ('1', 2.25), ('2', 2.75), ('3', 3.25), ('4', 3.5), ('5', 3.75), ('6', 4.0), ('7', 4.5),
         ('8', 5.0), ('9', 5.5), ('10', 6.0), ('10.5', 6.5), ('11', 8.0), ('13', 9.0), ('15', 10.0)]
    if direction == 'us->mm':
        v = str(value).strip().replace(' 1/2', '.5')
        for us, mm in T:
            if us == v:
                return mm
        return 'error: size'
    if direction == 'mm->us':
        x = Fraction(str(value))
        best = None
        for us, mm in T:
            d = abs(Fraction(str(mm)) - x)
            if d > Fraction(1, 2):
                continue
            if best is None or d < best[0] or (d == best[0] and us > best[1]):
                best = (d, us, mm)
        return best[1] if best else 'error: size'
    return 'error: direction' 
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['US 8', ['8', 'us->mm'], 5.0], ['half size', ['10 1/2', 'us->mm'], 6.5],
  ['tie between 9 and 10', [5.75, 'mm->us'], '10'],
  ['regression: tie-break toward larger', [6.25, 'mm->us'], '10.5'],
  ['generated control 1', [11, 'us->mm'], 8.0], ['generated control 2', [3.3, 'uk'], 'error: direction'],
  ['generated control 3', ['10.5', 'us->mm'], 6.5],
  ['generated control 4', [1.5, 'uk'], 'error: direction']],
 [['half size', ['10 1/2', 'us->mm'], 6.5], ['tie between 9 and 10', [5.75, 'mm->us'], '10'],
  ['edge of tolerance', [1.5, 'mm->us'], '0'],
  ['regression: tie-break toward larger', [3.625, 'mm->us'], '5'],
  ['generated control 1', ['0', 'us->mm'], 2.0], ['generated control 2', [3.0, 'mm->us'], '3'],
  ['generated control 3', [' 7 ', 'us->mm'], 4.5], ['generated control 4', ['8', 'us->mm'], 5.0]],
 [['tie between 9 and 10', [5.75, 'mm->us'], '10'], ['edge of tolerance', [1.5, 'mm->us'], '0'],
  ['no match', [11.0, 'mm->us'], 'error: size'],
  ['regression: tie-break toward larger', [6.25, 'mm->us'], '10.5'],
  ['generated control 1', [7.0, 'mm->us'], '10.5'], ['generated control 2', ['10.5', 'us->mm'], 6.5],
  ['generated control 3', [7.25, 'mm->us'], 'error: size'],
  ['generated control 4', ['12', 'us->mm'], 'error: size']],
 [['edge of tolerance', [1.5, 'mm->us'], '0'], ['no match', [11.0, 'mm->us'], 'error: size'],
  ['US 8', ['8', 'us->mm'], 5.0], ['regression: tie-break toward larger', [4.25, 'mm->us'], '7'],
  ['repair check: tie-break toward larger', [5.75, 'mm->us'], '10'],
  ['generated control 1', [7.0, 'mm->us'], '10.5'], ['generated control 2', ['10.5', 'us->mm'], 6.5],
  ['generated control 3', [1.5, 'uk'], 'error: direction']],
 [['no match', [11.0, 'mm->us'], 'error: size'], ['US 8', ['8', 'us->mm'], 5.0],
  ['half size', ['10 1/2', 'us->mm'], 6.5], ['regression: tie-break toward larger', [3.625, 'mm->us'], '5'],
  ['repair check: tie-break toward larger', [5.75, 'mm->us'], '10'],
  ['generated control 1', ['10.5', 'us->mm'], 6.5], ['generated control 2', ['12', 'us->mm'], 'error: size'],
  ['generated control 3', [3.3, 'mm->us'], '3']]]
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
US 85.05.0Passed
half size6.56.5Passed
tie between 9 and 10910Failed
regression: tie-break toward larger10.510.5Passed
generated control 18.08.0Passed
generated control 2error: directionerror: directionPassed
generated control 36.56.5Passed
generated control 4error: directionerror: directionPassed

SHA-256 / 100c799c37e5104ff29ed31ead52709ef1999c0c0b147d9575b1086e7bec3321

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(value, direction):
    T = [('0', 2.0), ('1', 2.25), ('2', 2.75), ('3', 3.25), ('4', 3.5), ('5', 3.75), ('6', 4.0), ('7', 4.5),
         ('8', 5.0), ('9', 5.5), ('10', 6.0), ('10.5', 6.5), ('11', 8.0), ('13', 9.0), ('15', 10.0)]
    if direction == 'us->mm':
        v = str(value).strip().replace(' 1/2', '.5')
        for us, mm in T:
            if us == v:
                return mm
        return 'error: size'
    if direction == 'mm->us':
        x = Fraction(str(value))
        best = None
        for us, mm in T:
            d = abs(Fraction(str(mm)) - x)
            if d > Fraction(1, 2):
                continue
            if best is None or d < best[0] or (d == best[0] and mm > best[2]):
                best = (d, us, mm)
        return best[1] if best else 'error: size'
    return 'error: direction' 
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['US 8', ['8', 'us->mm'], 5.0], ['half size', ['10 1/2', 'us->mm'], 6.5],
  ['tie between 9 and 10', [5.75, 'mm->us'], '10'],
  ['regression: tie-break toward larger', [6.25, 'mm->us'], '10.5'],
  ['generated control 1', [11, 'us->mm'], 8.0], ['generated control 2', [3.3, 'uk'], 'error: direction'],
  ['generated control 3', ['10.5', 'us->mm'], 6.5],
  ['generated control 4', [1.5, 'uk'], 'error: direction']],
 [['half size', ['10 1/2', 'us->mm'], 6.5], ['tie between 9 and 10', [5.75, 'mm->us'], '10'],
  ['edge of tolerance', [1.5, 'mm->us'], '0'],
  ['regression: tie-break toward larger', [3.625, 'mm->us'], '5'],
  ['generated control 1', ['0', 'us->mm'], 2.0], ['generated control 2', [3.0, 'mm->us'], '3'],
  ['generated control 3', [' 7 ', 'us->mm'], 4.5], ['generated control 4', ['8', 'us->mm'], 5.0]],
 [['tie between 9 and 10', [5.75, 'mm->us'], '10'], ['edge of tolerance', [1.5, 'mm->us'], '0'],
  ['no match', [11.0, 'mm->us'], 'error: size'],
  ['regression: tie-break toward larger', [6.25, 'mm->us'], '10.5'],
  ['generated control 1', [7.0, 'mm->us'], '10.5'], ['generated control 2', ['10.5', 'us->mm'], 6.5],
  ['generated control 3', [7.25, 'mm->us'], 'error: size'],
  ['generated control 4', ['12', 'us->mm'], 'error: size']],
 [['edge of tolerance', [1.5, 'mm->us'], '0'], ['no match', [11.0, 'mm->us'], 'error: size'],
  ['US 8', ['8', 'us->mm'], 5.0], ['regression: tie-break toward larger', [4.25, 'mm->us'], '7'],
  ['repair check: tie-break toward larger', [5.75, 'mm->us'], '10'],
  ['generated control 1', [7.0, 'mm->us'], '10.5'], ['generated control 2', ['10.5', 'us->mm'], 6.5],
  ['generated control 3', [1.5, 'uk'], 'error: direction']],
 [['no match', [11.0, 'mm->us'], 'error: size'], ['US 8', ['8', 'us->mm'], 5.0],
  ['half size', ['10 1/2', 'us->mm'], 6.5], ['regression: tie-break toward larger', [3.625, 'mm->us'], '5'],
  ['repair check: tie-break toward larger', [5.75, 'mm->us'], '10'],
  ['generated control 1', ['10.5', 'us->mm'], 6.5], ['generated control 2', ['12', 'us->mm'], 'error: size'],
  ['generated control 3', [3.3, 'mm->us'], '3']]]
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
US 85.05.0Passed
half size6.56.5Passed
tie between 9 and 101010Passed
regression: tie-break toward larger10.510.5Passed
generated control 18.08.0Passed
generated control 2error: directionerror: directionPassed
generated control 36.56.5Passed
generated control 4error: directionerror: directionPassed

SHA-256 / d170f5963493a27c3eda8a5b15371bcd804b9182abe07c8905758dc00d047ca3

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:35.134293+00:00.

Case digest / 04b9ca2afeb904df9b690e1b35d1cfd88e3c15932cb936b5cf505516532893ba