FA-97626 / Knitting and sewing pattern grading / Open access
Buttonhole row planner: top offset · case 01
The top buttonhole lands in the neckband.
ROOT CAUSE
The top offset is not subtracted from the band length.
VERIFIED REPAIR
Top position is band length minus top offset.
Unsuccessful approach: Subtracting both offsets double-counts the bottom offset.
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))
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: top offset', [40, 4, 2, 1, '36.5'], [5, 50, 95, 140]],
['repair check: top offset', [60, 7, 1.5, 1, '30'], [4, 33, 62, 90, 119, 148, 177]],
['generated control 1', [55.5, 7, 1, 4, '32'], [14, 41, 68, 95, 122, 148, 175]],
['generated control 2', [50, 4, 2, 4, '36.5'], [16, 69, 123, 176]],
['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: top offset', [40, 4, 3, 1, '30'], [4, 40, 76, 112]],
['repair check: top offset', [50, 5, 2, 2.5, '32'], [9, 45, 82, 118, 155]],
['generated control 1', [55.5, 2, 1.5, 2.5, '32'], [9, 174]],
['generated control 2', [40, 6, 3, 1, '28'], [4, 24, 44, 64, 84, 105]],
['generated control 3', [60, 0, 2, 2.5, '32'], 'error: count']],
[['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: top offset', [55.5, 7, 1.5, 4, '28'], [12, 36, 59, 82, 106, 129, 152]],
['repair check: top offset', [40, 6, 2, 1, '36.5'], [5, 32, 59, 86, 113, 140]],
['generated control 1', [50, 1, 3, 4, '32'], [151]],
['generated control 2', [55.5, 7, 2, 2, '36.5'], [8, 40, 71, 102, 134, 165, 196]],
['generated control 3', [55.5, 6, 1, 2.5, '30'], [9, 40, 71, 102, 133, 165]]],
[['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: top offset', [55.5, 5, 2, 4, '36.5'], [16, 61, 106, 151, 196]],
['repair check: top offset', [50, 7, 3, 2.5, '36.5'], [10, 37, 64, 91, 118, 145, 173]],
['generated control 1', [60, 1, 1, 2.5, '36.5'], [216]],
['generated control 2', [40, 6, 2, 4, '32'], [14, 36, 57, 79, 101, 123]],
['generated control 3', [50, 2, 3, 4, '28'], [12, 133]]],
[['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: top offset', [50, 6, 3, 2.5, '28'], [8, 33, 58, 83, 108, 133]],
['repair check: top offset', [62, 5, 2, 1, '36.5'], [5, 58, 112, 166, 220]],
['generated control 1', [50, 6, 1.5, 4, '28'], [12, 37, 62, 87, 112, 137]],
['generated control 2', [40, 5, 3, 1, '32'], [4, 33, 62, 91, 119]],
['generated control 3', [62, 2, 1.5, 1, '32'], [4, 195]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| five buttons | [7, 43, 79, 115, 151] | [7, 42, 77, 112, 147] | Failed |
| single button | [113] | [107] | Failed |
| no buttons | error: count | error: count | Passed |
| regression: top offset | [5, 52, 100, 147] | [5, 50, 95, 140] | Failed |
| repair check: top offset | [4, 34, 63, 93, 122, 152, 181] | [4, 33, 62, 90, 119, 148, 177] | Failed |
| generated control 1 | [14, 41, 69, 96, 124, 151, 179] | [14, 41, 68, 95, 122, 148, 175] | Failed |
| generated control 2 | [16, 72, 128, 184] | [16, 69, 123, 176] | Failed |
| generated control 3 | [4, 29, 54, 79, 104, 129] | [4, 29, 53, 77, 101, 126] | Failed |
SHA-256 / 842a401ecb838a497e35f1e2afc61346df600744b2798d041709e561fa69a877
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)) - Fraction(str(bottom_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: top offset', [40, 4, 2, 1, '36.5'], [5, 50, 95, 140]],
['repair check: top offset', [60, 7, 1.5, 1, '30'], [4, 33, 62, 90, 119, 148, 177]],
['generated control 1', [55.5, 7, 1, 4, '32'], [14, 41, 68, 95, 122, 148, 175]],
['generated control 2', [50, 4, 2, 4, '36.5'], [16, 69, 123, 176]],
['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: top offset', [40, 4, 3, 1, '30'], [4, 40, 76, 112]],
['repair check: top offset', [50, 5, 2, 2.5, '32'], [9, 45, 82, 118, 155]],
['generated control 1', [55.5, 2, 1.5, 2.5, '32'], [9, 174]],
['generated control 2', [40, 6, 3, 1, '28'], [4, 24, 44, 64, 84, 105]],
['generated control 3', [60, 0, 2, 2.5, '32'], 'error: count']],
[['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: top offset', [55.5, 7, 1.5, 4, '28'], [12, 36, 59, 82, 106, 129, 152]],
['repair check: top offset', [40, 6, 2, 1, '36.5'], [5, 32, 59, 86, 113, 140]],
['generated control 1', [50, 1, 3, 4, '32'], [151]],
['generated control 2', [55.5, 7, 2, 2, '36.5'], [8, 40, 71, 102, 134, 165, 196]],
['generated control 3', [55.5, 6, 1, 2.5, '30'], [9, 40, 71, 102, 133, 165]]],
[['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: top offset', [55.5, 5, 2, 4, '36.5'], [16, 61, 106, 151, 196]],
['repair check: top offset', [50, 7, 3, 2.5, '36.5'], [10, 37, 64, 91, 118, 145, 173]],
['generated control 1', [60, 1, 1, 2.5, '36.5'], [216]],
['generated control 2', [40, 6, 2, 4, '32'], [14, 36, 57, 79, 101, 123]],
['generated control 3', [50, 2, 3, 4, '28'], [12, 133]]],
[['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: top offset', [50, 6, 3, 2.5, '28'], [8, 33, 58, 83, 108, 133]],
['repair check: top offset', [62, 5, 2, 1, '36.5'], [5, 58, 112, 166, 220]],
['generated control 1', [50, 6, 1.5, 4, '28'], [12, 37, 62, 87, 112, 137]],
['generated control 2', [40, 5, 3, 1, '32'], [4, 33, 62, 91, 119]],
['generated control 3', [62, 2, 1.5, 1, '32'], [4, 195]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| five buttons | [7, 40, 74, 107, 141] | [7, 42, 77, 112, 147] | Failed |
| single button | [102] | [107] | Failed |
| no buttons | error: count | error: count | Passed |
| regression: top offset | [5, 48, 92, 136] | [5, 50, 95, 140] | Failed |
| repair check: top offset | [4, 32, 61, 89, 117, 145, 174] | [4, 33, 62, 90, 119, 148, 177] | Failed |
| generated control 1 | [14, 39, 63, 88, 113, 138, 163] | [14, 41, 68, 95, 122, 148, 175] | Failed |
| generated control 2 | [16, 64, 113, 162] | [16, 69, 123, 176] | Failed |
| generated control 3 | [4, 28, 52, 75, 99, 123] | [4, 29, 53, 77, 101, 126] | Failed |
SHA-256 / cbd3cc39bd472acf4c473e1e125395899b1cdd7e486e8bc3b4eddb5c784cced9
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: top offset', [40, 4, 2, 1, '36.5'], [5, 50, 95, 140]],
['repair check: top offset', [60, 7, 1.5, 1, '30'], [4, 33, 62, 90, 119, 148, 177]],
['generated control 1', [55.5, 7, 1, 4, '32'], [14, 41, 68, 95, 122, 148, 175]],
['generated control 2', [50, 4, 2, 4, '36.5'], [16, 69, 123, 176]],
['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: top offset', [40, 4, 3, 1, '30'], [4, 40, 76, 112]],
['repair check: top offset', [50, 5, 2, 2.5, '32'], [9, 45, 82, 118, 155]],
['generated control 1', [55.5, 2, 1.5, 2.5, '32'], [9, 174]],
['generated control 2', [40, 6, 3, 1, '28'], [4, 24, 44, 64, 84, 105]],
['generated control 3', [60, 0, 2, 2.5, '32'], 'error: count']],
[['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: top offset', [55.5, 7, 1.5, 4, '28'], [12, 36, 59, 82, 106, 129, 152]],
['repair check: top offset', [40, 6, 2, 1, '36.5'], [5, 32, 59, 86, 113, 140]],
['generated control 1', [50, 1, 3, 4, '32'], [151]],
['generated control 2', [55.5, 7, 2, 2, '36.5'], [8, 40, 71, 102, 134, 165, 196]],
['generated control 3', [55.5, 6, 1, 2.5, '30'], [9, 40, 71, 102, 133, 165]]],
[['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: top offset', [55.5, 5, 2, 4, '36.5'], [16, 61, 106, 151, 196]],
['repair check: top offset', [50, 7, 3, 2.5, '36.5'], [10, 37, 64, 91, 118, 145, 173]],
['generated control 1', [60, 1, 1, 2.5, '36.5'], [216]],
['generated control 2', [40, 6, 2, 4, '32'], [14, 36, 57, 79, 101, 123]],
['generated control 3', [50, 2, 3, 4, '28'], [12, 133]]],
[['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: top offset', [50, 6, 3, 2.5, '28'], [8, 33, 58, 83, 108, 133]],
['repair check: top offset', [62, 5, 2, 1, '36.5'], [5, 58, 112, 166, 220]],
['generated control 1', [50, 6, 1.5, 4, '28'], [12, 37, 62, 87, 112, 137]],
['generated control 2', [40, 5, 3, 1, '32'], [4, 33, 62, 91, 119]],
['generated control 3', [62, 2, 1.5, 1, '32'], [4, 195]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| five buttons | [7, 42, 77, 112, 147] | [7, 42, 77, 112, 147] | Passed |
| single button | [107] | [107] | Passed |
| no buttons | error: count | error: count | Passed |
| regression: top offset | [5, 50, 95, 140] | [5, 50, 95, 140] | Passed |
| repair check: top offset | [4, 33, 62, 90, 119, 148, 177] | [4, 33, 62, 90, 119, 148, 177] | Passed |
| generated control 1 | [14, 41, 68, 95, 122, 148, 175] | [14, 41, 68, 95, 122, 148, 175] | Passed |
| generated control 2 | [16, 69, 123, 176] | [16, 69, 123, 176] | Passed |
| generated control 3 | [4, 29, 53, 77, 101, 126] | [4, 29, 53, 77, 101, 126] | Passed |
SHA-256 / dc3a07f2e93bfce0c93900b830145022bd60e624234a85dd0561d15d3dfa29ab
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.003807+00:00.
Case digest / bdeaf0d65dd53fa4d7dc0da728e094f6047a30c0dba260da83d5ba897297af85