FAILURE MAP
← Case archive

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

Buttonhole row planner: spacing divisor · case 01

The top buttonhole falls well below the neck.

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

ROOT CAUSE

Spacing divides by the number of buttons instead of the number of gaps.

VERIFIED REPAIR

Divide the span by n-1.

Unsuccessful approach: n+1 gaps treat the offsets as extra buttons.

Case contract

Buttonholes run from bottom_offset to band_len - top_offset (cm), evenly spaced; a single buttonhole goes at the top position. Convert each position to a 1-based row: half-up of position*rows_per_10cm/10, plus 1. n < 1 -> "error: count"; top position below bottom -> "error: offsets".

Why this case matters

Cardigan button bands place buttonholes evenly between neck and hem offsets.

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(band_len, n, top_offset, bottom_offset, rows_per_10cm):
    if n < 1:
        return 'error: count'
    lo = Fraction(str(bottom_offset))
    hi = Fraction(str(band_len)) - Fraction(str(top_offset))
    if hi < lo:
        return 'error: offsets'
    g = Fraction(str(rows_per_10cm)) / 10
    if n == 1:
        pos = [hi]
    else:
        gap = (hi - lo) / n
        pos = [lo + i * gap for i in range(n)]
    return [math.floor(p * g + Fraction(1, 2)) + 1 for p in pos]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['regression: spacing divisor', [55.5, 5, 2, 2.5, '36.5'], [10, 57, 103, 150, 196]],
  ['repair check: spacing divisor', [60, 6, 2, 1, '32'], [4, 41, 77, 114, 150, 187]],
  ['generated control 1', [60, 1, 2, 1, '28'], [163]],
  ['generated control 2', [50, 6, 1, 1, '36.5'], [5, 40, 75, 110, 145, 180]],
  ['generated control 3', [50, 1, 1.5, 2.5, '28'], [137]]],
 [['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['regression: spacing divisor', [55.5, 2, 2, 4, '32'], [14, 172]],
  ['repair check: spacing divisor', [60, 4, 1, 4, '32'], [14, 72, 131, 190]],
  ['generated control 1', [50, 1, 1.5, 1, '36.5'], [178]],
  ['generated control 2', [40, 0, 3, 4, '28'], 'error: count'],
  ['generated control 3', [60, 1, 1.5, 4, '30'], [177]]],
 [['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]],
  ['regression: spacing divisor', [60, 2, 1, 1, '30'], [4, 178]],
  ['repair check: spacing divisor', [50, 7, 1, 4, '30'], [13, 36, 58, 81, 103, 126, 148]],
  ['generated control 1', [62, 0, 1, 4, '30'], 'error: count'],
  ['generated control 2', [60, 1, 3, 2, '32'], [183]],
  ['generated control 3', [62, 4, 1.5, 2, '30'], [7, 66, 124, 183]]],
 [['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['regression: spacing divisor', [40, 4, 1, 4, '28'], [12, 45, 78, 110]],
  ['repair check: spacing divisor', [40, 7, 3, 4, '30'], [13, 30, 46, 63, 79, 96, 112]],
  ['generated control 1', [40, 1, 3, 4, '30'], [112]],
  ['generated control 2', [55.5, 0, 1.5, 4, '28'], 'error: count'],
  ['generated control 3', [40, 6, 1, 1, '32'], [4, 29, 53, 77, 101, 126]]],
 [['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['regression: spacing divisor', [40, 6, 1.5, 2, '36.5'], [8, 35, 62, 88, 115, 142]],
  ['repair check: spacing divisor', [40, 2, 2, 1, '30'], [4, 115]],
  ['generated control 1', [55.5, 5, 3, 4, '32'], [14, 53, 91, 130, 169]],
  ['generated control 2', [55.5, 7, 1, 2.5, '36.5'], [10, 42, 73, 105, 137, 168, 200]],
  ['generated control 3', [40, 5, 2, 2, '36.5'], [8, 41, 74, 107, 140]]]]
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
five buttons[7, 35, 63, 91, 119][7, 42, 77, 112, 147]Failed
single button[107][107]Passed
no buttonserror: counterror: countPassed
regression: spacing divisor[10, 47, 85, 122, 159][10, 57, 103, 150, 196]Failed
repair check: spacing divisor[4, 35, 65, 95, 126, 156][4, 41, 77, 114, 150, 187]Failed
generated control 1[163][163]Passed
generated control 2[5, 34, 63, 92, 121, 151][5, 40, 75, 110, 145, 180]Failed
generated control 3[137][137]Passed

SHA-256 / 350899820294251c2b711aee2ac4b291385964cbb479c835fcce1796f524771c

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(band_len, n, top_offset, bottom_offset, rows_per_10cm):
    if n < 1:
        return 'error: count'
    lo = Fraction(str(bottom_offset))
    hi = Fraction(str(band_len)) - Fraction(str(top_offset))
    if hi < lo:
        return 'error: offsets'
    g = Fraction(str(rows_per_10cm)) / 10
    if n == 1:
        pos = [hi]
    else:
        gap = (hi - lo) / (n + 1)
        pos = [lo + i * gap for i in range(n)]
    return [math.floor(p * g + Fraction(1, 2)) + 1 for p in pos]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['regression: spacing divisor', [55.5, 5, 2, 2.5, '36.5'], [10, 57, 103, 150, 196]],
  ['repair check: spacing divisor', [60, 6, 2, 1, '32'], [4, 41, 77, 114, 150, 187]],
  ['generated control 1', [60, 1, 2, 1, '28'], [163]],
  ['generated control 2', [50, 6, 1, 1, '36.5'], [5, 40, 75, 110, 145, 180]],
  ['generated control 3', [50, 1, 1.5, 2.5, '28'], [137]]],
 [['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['regression: spacing divisor', [55.5, 2, 2, 4, '32'], [14, 172]],
  ['repair check: spacing divisor', [60, 4, 1, 4, '32'], [14, 72, 131, 190]],
  ['generated control 1', [50, 1, 1.5, 1, '36.5'], [178]],
  ['generated control 2', [40, 0, 3, 4, '28'], 'error: count'],
  ['generated control 3', [60, 1, 1.5, 4, '30'], [177]]],
 [['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]],
  ['regression: spacing divisor', [60, 2, 1, 1, '30'], [4, 178]],
  ['repair check: spacing divisor', [50, 7, 1, 4, '30'], [13, 36, 58, 81, 103, 126, 148]],
  ['generated control 1', [62, 0, 1, 4, '30'], 'error: count'],
  ['generated control 2', [60, 1, 3, 2, '32'], [183]],
  ['generated control 3', [62, 4, 1.5, 2, '30'], [7, 66, 124, 183]]],
 [['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['regression: spacing divisor', [40, 4, 1, 4, '28'], [12, 45, 78, 110]],
  ['repair check: spacing divisor', [40, 7, 3, 4, '30'], [13, 30, 46, 63, 79, 96, 112]],
  ['generated control 1', [40, 1, 3, 4, '30'], [112]],
  ['generated control 2', [55.5, 0, 1.5, 4, '28'], 'error: count'],
  ['generated control 3', [40, 6, 1, 1, '32'], [4, 29, 53, 77, 101, 126]]],
 [['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['regression: spacing divisor', [40, 6, 1.5, 2, '36.5'], [8, 35, 62, 88, 115, 142]],
  ['repair check: spacing divisor', [40, 2, 2, 1, '30'], [4, 115]],
  ['generated control 1', [55.5, 5, 3, 4, '32'], [14, 53, 91, 130, 169]],
  ['generated control 2', [55.5, 7, 1, 2.5, '36.5'], [10, 42, 73, 105, 137, 168, 200]],
  ['generated control 3', [40, 5, 2, 2, '36.5'], [8, 41, 74, 107, 140]]]]
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
five buttons[7, 30, 54, 77, 100][7, 42, 77, 112, 147]Failed
single button[107][107]Passed
no buttonserror: counterror: countPassed
regression: spacing divisor[10, 41, 72, 103, 134][10, 57, 103, 150, 196]Failed
repair check: spacing divisor[4, 30, 56, 82, 108, 134][4, 41, 77, 114, 150, 187]Failed
generated control 1[163][163]Passed
generated control 2[5, 30, 55, 80, 105, 130][5, 40, 75, 110, 145, 180]Failed
generated control 3[137][137]Passed

SHA-256 / e55456f579b51410a19ea889d34554f838be298f7f86d9648229f0a9a136b273

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(band_len, n, top_offset, bottom_offset, rows_per_10cm):
    if n < 1:
        return 'error: count'
    lo = Fraction(str(bottom_offset))
    hi = Fraction(str(band_len)) - Fraction(str(top_offset))
    if hi < lo:
        return 'error: offsets'
    g = Fraction(str(rows_per_10cm)) / 10
    if n == 1:
        pos = [hi]
    else:
        gap = (hi - lo) / (n - 1)
        pos = [lo + i * gap for i in range(n)]
    return [math.floor(p * g + Fraction(1, 2)) + 1 for p in pos]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['regression: spacing divisor', [55.5, 5, 2, 2.5, '36.5'], [10, 57, 103, 150, 196]],
  ['repair check: spacing divisor', [60, 6, 2, 1, '32'], [4, 41, 77, 114, 150, 187]],
  ['generated control 1', [60, 1, 2, 1, '28'], [163]],
  ['generated control 2', [50, 6, 1, 1, '36.5'], [5, 40, 75, 110, 145, 180]],
  ['generated control 3', [50, 1, 1.5, 2.5, '28'], [137]]],
 [['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['regression: spacing divisor', [55.5, 2, 2, 4, '32'], [14, 172]],
  ['repair check: spacing divisor', [60, 4, 1, 4, '32'], [14, 72, 131, 190]],
  ['generated control 1', [50, 1, 1.5, 1, '36.5'], [178]],
  ['generated control 2', [40, 0, 3, 4, '28'], 'error: count'],
  ['generated control 3', [60, 1, 1.5, 4, '30'], [177]]],
 [['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]],
  ['regression: spacing divisor', [60, 2, 1, 1, '30'], [4, 178]],
  ['repair check: spacing divisor', [50, 7, 1, 4, '30'], [13, 36, 58, 81, 103, 126, 148]],
  ['generated control 1', [62, 0, 1, 4, '30'], 'error: count'],
  ['generated control 2', [60, 1, 3, 2, '32'], [183]],
  ['generated control 3', [62, 4, 1.5, 2, '30'], [7, 66, 124, 183]]],
 [['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['regression: spacing divisor', [40, 4, 1, 4, '28'], [12, 45, 78, 110]],
  ['repair check: spacing divisor', [40, 7, 3, 4, '30'], [13, 30, 46, 63, 79, 96, 112]],
  ['generated control 1', [40, 1, 3, 4, '30'], [112]],
  ['generated control 2', [55.5, 0, 1.5, 4, '28'], 'error: count'],
  ['generated control 3', [40, 6, 1, 1, '32'], [4, 29, 53, 77, 101, 126]]],
 [['single button', [40, 1, 2, 2, '28'], [107]], ['no buttons', [40, 0, 2, 2, '28'], 'error: count'],
  ['five buttons', [50, 5, 1.5, 2, '30'], [7, 42, 77, 112, 147]],
  ['regression: spacing divisor', [40, 6, 1.5, 2, '36.5'], [8, 35, 62, 88, 115, 142]],
  ['repair check: spacing divisor', [40, 2, 2, 1, '30'], [4, 115]],
  ['generated control 1', [55.5, 5, 3, 4, '32'], [14, 53, 91, 130, 169]],
  ['generated control 2', [55.5, 7, 1, 2.5, '36.5'], [10, 42, 73, 105, 137, 168, 200]],
  ['generated control 3', [40, 5, 2, 2, '36.5'], [8, 41, 74, 107, 140]]]]
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
five buttons[7, 42, 77, 112, 147][7, 42, 77, 112, 147]Passed
single button[107][107]Passed
no buttonserror: counterror: countPassed
regression: spacing divisor[10, 57, 103, 150, 196][10, 57, 103, 150, 196]Passed
repair check: spacing divisor[4, 41, 77, 114, 150, 187][4, 41, 77, 114, 150, 187]Passed
generated control 1[163][163]Passed
generated control 2[5, 40, 75, 110, 145, 180][5, 40, 75, 110, 145, 180]Passed
generated control 3[137][137]Passed

SHA-256 / d5a97ea209a304b1d567f99f65fbfb7ec4f96dc5f598984812ca1765b8895da4

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

Case digest / a4c784a2128a14a3a722b2bd5cb5b720ce5daa044cc984417711674bb877cf19