FAILURE MAP
← Case archive

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

Buttonhole row planner: one-based rows · case 01

The first buttonhole is placed one row too low.

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

ROOT CAUSE

Row numbers are 0-based.

VERIFIED REPAIR

Add 1 to the rounded row offset.

Unsuccessful approach: Ceiling the offset is not the same as adding one row.

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 - 1)
        pos = [lo + i * gap for i in range(n)]
    return [math.floor(p * g + Fraction(1, 2)) 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: one-based rows', [60, 2, 2, 4, '32'], [14, 187]],
  ['repair check: one-based rows', [40, 5, 2, 4, '36.5'], [16, 47, 78, 109, 140]],
  ['generated control 1', [40, 4, 1.5, 1, '32'], [4, 44, 84, 124]],
  ['generated control 2', [55.5, 2, 3, 2, '28'], [7, 148]],
  ['generated control 3', [50, 6, 2, 2.5, '28'], [8, 33, 59, 84, 110, 135]]],
 [['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: one-based rows', [40, 2, 2, 2.5, '32'], [9, 123]],
  ['repair check: one-based rows', [60, 6, 1.5, 2.5, '30'], [9, 42, 76, 109, 143, 177]],
  ['generated control 1', [40, 0, 3, 2, '28'], 'error: count'],
  ['generated control 2', [62, 4, 1, 1, '36.5'], [5, 78, 151, 224]],
  ['generated control 3', [62, 5, 1, 4, '28'], [12, 52, 92, 132, 172]]],
 [['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: one-based rows', [40, 5, 1, 2, '36.5'], [8, 42, 76, 110, 143]],
  ['repair check: one-based rows', [60, 7, 1, 4, '32'], [14, 43, 72, 102, 131, 160, 190]],
  ['generated control 1', [62, 5, 3, 2.5, '36.5'], [10, 62, 113, 165, 216]],
  ['generated control 2', [62, 1, 1, 2, '30'], [184]],
  ['generated control 3', [40, 7, 2, 4, '28'], [12, 28, 44, 60, 76, 92, 107]]],
 [['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: one-based rows', [50, 6, 1, 4, '32'], [14, 43, 71, 100, 129, 158]],
  ['repair check: one-based rows', [62, 5, 3, 2.5, '36.5'], [10, 62, 113, 165, 216]],
  ['generated control 1', [40, 6, 3, 2, '36.5'], [8, 34, 59, 85, 111, 136]],
  ['generated control 2', [40, 0, 3, 1, '36.5'], 'error: count'],
  ['generated control 3', [55.5, 7, 1, 4, '32'], [14, 41, 68, 95, 122, 148, 175]]],
 [['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: one-based rows', [55.5, 7, 3, 2.5, '30'], [9, 34, 59, 84, 109, 134, 159]],
  ['repair check: one-based rows', [60, 6, 1, 2.5, '32'], [9, 45, 81, 117, 154, 190]],
  ['generated control 1', [62, 1, 1.5, 4, '32'], [195]],
  ['generated control 2', [60, 7, 3, 4, '36.5'], [16, 48, 80, 112, 145, 177, 209]],
  ['generated control 3', [40, 5, 1, 1, '28'], [4, 30, 57, 84, 110]]]]
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[6, 41, 76, 111, 146][7, 42, 77, 112, 147]Failed
single button[106][107]Failed
no buttonserror: counterror: countPassed
regression: one-based rows[13, 186][14, 187]Failed
repair check: one-based rows[15, 46, 77, 108, 139][16, 47, 78, 109, 140]Failed
generated control 1[3, 43, 83, 123][4, 44, 84, 124]Failed
generated control 2[6, 147][7, 148]Failed
generated control 3[7, 32, 58, 83, 109, 134][8, 33, 59, 84, 110, 135]Failed

SHA-256 / 07e8a9cb2e333144a81ebf2cf8d320a002c1e4f509a72b9ea7c51327f052feea

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.ceil(p * g) 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: one-based rows', [60, 2, 2, 4, '32'], [14, 187]],
  ['repair check: one-based rows', [40, 5, 2, 4, '36.5'], [16, 47, 78, 109, 140]],
  ['generated control 1', [40, 4, 1.5, 1, '32'], [4, 44, 84, 124]],
  ['generated control 2', [55.5, 2, 3, 2, '28'], [7, 148]],
  ['generated control 3', [50, 6, 2, 2.5, '28'], [8, 33, 59, 84, 110, 135]]],
 [['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: one-based rows', [40, 2, 2, 2.5, '32'], [9, 123]],
  ['repair check: one-based rows', [60, 6, 1.5, 2.5, '30'], [9, 42, 76, 109, 143, 177]],
  ['generated control 1', [40, 0, 3, 2, '28'], 'error: count'],
  ['generated control 2', [62, 4, 1, 1, '36.5'], [5, 78, 151, 224]],
  ['generated control 3', [62, 5, 1, 4, '28'], [12, 52, 92, 132, 172]]],
 [['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: one-based rows', [40, 5, 1, 2, '36.5'], [8, 42, 76, 110, 143]],
  ['repair check: one-based rows', [60, 7, 1, 4, '32'], [14, 43, 72, 102, 131, 160, 190]],
  ['generated control 1', [62, 5, 3, 2.5, '36.5'], [10, 62, 113, 165, 216]],
  ['generated control 2', [62, 1, 1, 2, '30'], [184]],
  ['generated control 3', [40, 7, 2, 4, '28'], [12, 28, 44, 60, 76, 92, 107]]],
 [['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: one-based rows', [50, 6, 1, 4, '32'], [14, 43, 71, 100, 129, 158]],
  ['repair check: one-based rows', [62, 5, 3, 2.5, '36.5'], [10, 62, 113, 165, 216]],
  ['generated control 1', [40, 6, 3, 2, '36.5'], [8, 34, 59, 85, 111, 136]],
  ['generated control 2', [40, 0, 3, 1, '36.5'], 'error: count'],
  ['generated control 3', [55.5, 7, 1, 4, '32'], [14, 41, 68, 95, 122, 148, 175]]],
 [['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: one-based rows', [55.5, 7, 3, 2.5, '30'], [9, 34, 59, 84, 109, 134, 159]],
  ['repair check: one-based rows', [60, 6, 1, 2.5, '32'], [9, 45, 81, 117, 154, 190]],
  ['generated control 1', [62, 1, 1.5, 4, '32'], [195]],
  ['generated control 2', [60, 7, 3, 4, '36.5'], [16, 48, 80, 112, 145, 177, 209]],
  ['generated control 3', [40, 5, 1, 1, '28'], [4, 30, 57, 84, 110]]]]
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[6, 41, 76, 111, 146][7, 42, 77, 112, 147]Failed
single button[107][107]Passed
no buttonserror: counterror: countPassed
regression: one-based rows[13, 186][14, 187]Failed
repair check: one-based rows[15, 46, 77, 108, 139][16, 47, 78, 109, 140]Failed
generated control 1[4, 44, 84, 124][4, 44, 84, 124]Passed
generated control 2[6, 147][7, 148]Failed
generated control 3[7, 33, 58, 84, 109, 135][8, 33, 59, 84, 110, 135]Failed

SHA-256 / 6693349ea8c677c3c7b712624c00bd00f35d86977695a39a8d8f0b64ed24d907

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: one-based rows', [60, 2, 2, 4, '32'], [14, 187]],
  ['repair check: one-based rows', [40, 5, 2, 4, '36.5'], [16, 47, 78, 109, 140]],
  ['generated control 1', [40, 4, 1.5, 1, '32'], [4, 44, 84, 124]],
  ['generated control 2', [55.5, 2, 3, 2, '28'], [7, 148]],
  ['generated control 3', [50, 6, 2, 2.5, '28'], [8, 33, 59, 84, 110, 135]]],
 [['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: one-based rows', [40, 2, 2, 2.5, '32'], [9, 123]],
  ['repair check: one-based rows', [60, 6, 1.5, 2.5, '30'], [9, 42, 76, 109, 143, 177]],
  ['generated control 1', [40, 0, 3, 2, '28'], 'error: count'],
  ['generated control 2', [62, 4, 1, 1, '36.5'], [5, 78, 151, 224]],
  ['generated control 3', [62, 5, 1, 4, '28'], [12, 52, 92, 132, 172]]],
 [['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: one-based rows', [40, 5, 1, 2, '36.5'], [8, 42, 76, 110, 143]],
  ['repair check: one-based rows', [60, 7, 1, 4, '32'], [14, 43, 72, 102, 131, 160, 190]],
  ['generated control 1', [62, 5, 3, 2.5, '36.5'], [10, 62, 113, 165, 216]],
  ['generated control 2', [62, 1, 1, 2, '30'], [184]],
  ['generated control 3', [40, 7, 2, 4, '28'], [12, 28, 44, 60, 76, 92, 107]]],
 [['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: one-based rows', [50, 6, 1, 4, '32'], [14, 43, 71, 100, 129, 158]],
  ['repair check: one-based rows', [62, 5, 3, 2.5, '36.5'], [10, 62, 113, 165, 216]],
  ['generated control 1', [40, 6, 3, 2, '36.5'], [8, 34, 59, 85, 111, 136]],
  ['generated control 2', [40, 0, 3, 1, '36.5'], 'error: count'],
  ['generated control 3', [55.5, 7, 1, 4, '32'], [14, 41, 68, 95, 122, 148, 175]]],
 [['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: one-based rows', [55.5, 7, 3, 2.5, '30'], [9, 34, 59, 84, 109, 134, 159]],
  ['repair check: one-based rows', [60, 6, 1, 2.5, '32'], [9, 45, 81, 117, 154, 190]],
  ['generated control 1', [62, 1, 1.5, 4, '32'], [195]],
  ['generated control 2', [60, 7, 3, 4, '36.5'], [16, 48, 80, 112, 145, 177, 209]],
  ['generated control 3', [40, 5, 1, 1, '28'], [4, 30, 57, 84, 110]]]]
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: one-based rows[14, 187][14, 187]Passed
repair check: one-based rows[16, 47, 78, 109, 140][16, 47, 78, 109, 140]Passed
generated control 1[4, 44, 84, 124][4, 44, 84, 124]Passed
generated control 2[7, 148][7, 148]Passed
generated control 3[8, 33, 59, 84, 110, 135][8, 33, 59, 84, 110, 135]Passed

SHA-256 / 5db4e5169bcef7c6afc0a4f194287762cf402d4ed37879010988010b68b394c9

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

Case digest / 14b35815ffb57a4f80fd1e700be7dbbf8b44b8d6c91d212d2b1234b9114c31b7