FA-97341 / Knitting and sewing pattern grading / Open access
Gauge-based cast-on calculator: ease sign · case 01
A sweater with 10 cm positive ease comes out 10 cm narrower than the body.
ROOT CAUSE
Ease is subtracted from the body measurement instead of added.
VERIFIED REPAIR
Add ease (positive or negative) to the body width.
Unsuccessful approach: Adding half the ease treats it as per-side ease for a piece that is already one full width.
Case contract
Target width = width + ease (ease may be negative). Raw stitches = target * gauge / 10 (gauge per 10 cm). Body stitches = nearest multiple of repeat, half-up in repeat units, at least one repeat. Cast on = body + 2*edge (selvedge each side). Actual body width = body*10/gauge, half-up to 0.1 cm. Non-positive target, gauge or repeat returns an error string.
Why this case matters
Knitting patterns and calculators convert a finished measurement and gauge into a stitch count that fits the stitch pattern repeat.
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(width_cm, sts_per_10cm, repeat, edge, ease_cm):
w = Fraction(str(width_cm)) - Fraction(str(ease_cm))
g = Fraction(str(sts_per_10cm))
if w <= 0 or g <= 0 or repeat < 1:
return 'error: bad measurements'
raw = w * g / 10
body = repeat * math.floor(raw / repeat + Fraction(1, 2))
body = max(repeat, body)
total = body + 2 * edge
actual = body * 10 / g
return [total, math.floor(actual * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['regression: ease sign', [3, '18', 8, 1, 10], [26, 13.3]],
['repair check: ease sign', [40, '27.5', 2, 3, 7.5], [136, 47.3]],
['generated control 1', [3, '22', 6, 3, 7.5], [30, 10.9]],
['generated control 2', [30, '27.5', 12, 3, 0], [90, 30.5]],
['generated control 3', [52.5, '24', 2, 2, 7.5], [148, 60.0]]],
[['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['regression: ease sign', [50, '27.5', 8, 2, -5], [124, 43.6]],
['repair check: ease sign', [10, '24', 1, 3, 10], [54, 20.0]],
['generated control 1', [30, '27.5', 12, 2, 0], [88, 30.5]],
['generated control 2', [30, '22', 1, 3, 0], [72, 30.0]],
['generated control 3', [45, '24', 1, 0, -5], [96, 40.0]]],
[['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['regression: ease sign', [30, '20', 12, 0, 10], [84, 42.0]],
['repair check: ease sign', [10, '22', 8, 3, -5], [14, 3.6]],
['generated control 1', [3, '22', 6, 1, -2], [8, 2.7]],
['generated control 2', [30, '20', 12, 3, 0], [66, 30.0]],
['generated control 3', [50, '27.5', 2, 0, 0], [138, 50.2]]],
[['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['regression: ease sign', [52.5, '24', 6, 3, 5], [144, 57.5]],
['repair check: ease sign', [50, '16', 12, 0, 10], [96, 60.0]],
['generated control 1', [10, '20', 12, 2, 5], [40, 18.0]],
['generated control 2', [45, '27.5', 6, 2, 10], [154, 54.5]],
['generated control 3', [40, '22', 4, 2, -5], [80, 34.5]]],
[['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['regression: ease sign', [52.5, '20', 1, 3, -5], [101, 47.5]],
['repair check: ease sign', [50, '20', 6, 3, 7.5], [120, 57.0]],
['generated control 1', [50, '16', 4, 1, -2], [78, 47.5]],
['generated control 2', [8, '24', 12, 3, -5], [18, 5.0]],
['generated control 3', [8, '18', 8, 0, 10], [32, 17.8]]]]
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 |
|---|---|---|---|
| plain stockinette | [100, 50.0] | [100, 50.0] | Passed |
| rib with selvedges | [90, 40.0] | [114, 50.9] | Failed |
| narrow strap one repeat | [12, 5.0] | [12, 5.0] | Passed |
| regression: ease sign | error: bad measurements | [26, 13.3] | Failed |
| repair check: ease sign | [96, 32.7] | [136, 47.3] | Failed |
| generated control 1 | error: bad measurements | [30, 10.9] | Failed |
| generated control 2 | [90, 30.5] | [90, 30.5] | Passed |
| generated control 3 | [112, 45.0] | [148, 60.0] | Failed |
SHA-256 / cc9278efffe6a36f966d59a6e083e2fa598ece4782349d55494fd18363e5b50d
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(width_cm, sts_per_10cm, repeat, edge, ease_cm):
w = Fraction(str(width_cm)) + Fraction(str(ease_cm)) / 2
g = Fraction(str(sts_per_10cm))
if w <= 0 or g <= 0 or repeat < 1:
return 'error: bad measurements'
raw = w * g / 10
body = repeat * math.floor(raw / repeat + Fraction(1, 2))
body = max(repeat, body)
total = body + 2 * edge
actual = body * 10 / g
return [total, math.floor(actual * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['regression: ease sign', [3, '18', 8, 1, 10], [26, 13.3]],
['repair check: ease sign', [40, '27.5', 2, 3, 7.5], [136, 47.3]],
['generated control 1', [3, '22', 6, 3, 7.5], [30, 10.9]],
['generated control 2', [30, '27.5', 12, 3, 0], [90, 30.5]],
['generated control 3', [52.5, '24', 2, 2, 7.5], [148, 60.0]]],
[['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['regression: ease sign', [50, '27.5', 8, 2, -5], [124, 43.6]],
['repair check: ease sign', [10, '24', 1, 3, 10], [54, 20.0]],
['generated control 1', [30, '27.5', 12, 2, 0], [88, 30.5]],
['generated control 2', [30, '22', 1, 3, 0], [72, 30.0]],
['generated control 3', [45, '24', 1, 0, -5], [96, 40.0]]],
[['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['regression: ease sign', [30, '20', 12, 0, 10], [84, 42.0]],
['repair check: ease sign', [10, '22', 8, 3, -5], [14, 3.6]],
['generated control 1', [3, '22', 6, 1, -2], [8, 2.7]],
['generated control 2', [30, '20', 12, 3, 0], [66, 30.0]],
['generated control 3', [50, '27.5', 2, 0, 0], [138, 50.2]]],
[['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['regression: ease sign', [52.5, '24', 6, 3, 5], [144, 57.5]],
['repair check: ease sign', [50, '16', 12, 0, 10], [96, 60.0]],
['generated control 1', [10, '20', 12, 2, 5], [40, 18.0]],
['generated control 2', [45, '27.5', 6, 2, 10], [154, 54.5]],
['generated control 3', [40, '22', 4, 2, -5], [80, 34.5]]],
[['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['regression: ease sign', [52.5, '20', 1, 3, -5], [101, 47.5]],
['repair check: ease sign', [50, '20', 6, 3, 7.5], [120, 57.0]],
['generated control 1', [50, '16', 4, 1, -2], [78, 47.5]],
['generated control 2', [8, '24', 12, 3, -5], [18, 5.0]],
['generated control 3', [8, '18', 8, 0, 10], [32, 17.8]]]]
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 |
|---|---|---|---|
| plain stockinette | [100, 50.0] | [100, 50.0] | Passed |
| rib with selvedges | [106, 47.3] | [114, 50.9] | Failed |
| narrow strap one repeat | [12, 5.0] | [12, 5.0] | Passed |
| regression: ease sign | [18, 8.9] | [26, 13.3] | Failed |
| repair check: ease sign | [126, 43.6] | [136, 47.3] | Failed |
| generated control 1 | [18, 5.5] | [30, 10.9] | Failed |
| generated control 2 | [90, 30.5] | [90, 30.5] | Passed |
| generated control 3 | [140, 56.7] | [148, 60.0] | Failed |
SHA-256 / 72e75a1d288cdfdbd2a5d08aeda22546552a4e0fdd5c90daa1f8689a186e0cda
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(width_cm, sts_per_10cm, repeat, edge, ease_cm):
w = Fraction(str(width_cm)) + Fraction(str(ease_cm))
g = Fraction(str(sts_per_10cm))
if w <= 0 or g <= 0 or repeat < 1:
return 'error: bad measurements'
raw = w * g / 10
body = repeat * math.floor(raw / repeat + Fraction(1, 2))
body = max(repeat, body)
total = body + 2 * edge
actual = body * 10 / g
return [total, math.floor(actual * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['regression: ease sign', [3, '18', 8, 1, 10], [26, 13.3]],
['repair check: ease sign', [40, '27.5', 2, 3, 7.5], [136, 47.3]],
['generated control 1', [3, '22', 6, 3, 7.5], [30, 10.9]],
['generated control 2', [30, '27.5', 12, 3, 0], [90, 30.5]],
['generated control 3', [52.5, '24', 2, 2, 7.5], [148, 60.0]]],
[['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['regression: ease sign', [50, '27.5', 8, 2, -5], [124, 43.6]],
['repair check: ease sign', [10, '24', 1, 3, 10], [54, 20.0]],
['generated control 1', [30, '27.5', 12, 2, 0], [88, 30.5]],
['generated control 2', [30, '22', 1, 3, 0], [72, 30.0]],
['generated control 3', [45, '24', 1, 0, -5], [96, 40.0]]],
[['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['regression: ease sign', [30, '20', 12, 0, 10], [84, 42.0]],
['repair check: ease sign', [10, '22', 8, 3, -5], [14, 3.6]],
['generated control 1', [3, '22', 6, 1, -2], [8, 2.7]],
['generated control 2', [30, '20', 12, 3, 0], [66, 30.0]],
['generated control 3', [50, '27.5', 2, 0, 0], [138, 50.2]]],
[['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['regression: ease sign', [52.5, '24', 6, 3, 5], [144, 57.5]],
['repair check: ease sign', [50, '16', 12, 0, 10], [96, 60.0]],
['generated control 1', [10, '20', 12, 2, 5], [40, 18.0]],
['generated control 2', [45, '27.5', 6, 2, 10], [154, 54.5]],
['generated control 3', [40, '22', 4, 2, -5], [80, 34.5]]],
[['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['regression: ease sign', [52.5, '20', 1, 3, -5], [101, 47.5]],
['repair check: ease sign', [50, '20', 6, 3, 7.5], [120, 57.0]],
['generated control 1', [50, '16', 4, 1, -2], [78, 47.5]],
['generated control 2', [8, '24', 12, 3, -5], [18, 5.0]],
['generated control 3', [8, '18', 8, 0, 10], [32, 17.8]]]]
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 |
|---|---|---|---|
| plain stockinette | [100, 50.0] | [100, 50.0] | Passed |
| rib with selvedges | [114, 50.9] | [114, 50.9] | Passed |
| narrow strap one repeat | [12, 5.0] | [12, 5.0] | Passed |
| regression: ease sign | [26, 13.3] | [26, 13.3] | Passed |
| repair check: ease sign | [136, 47.3] | [136, 47.3] | Passed |
| generated control 1 | [30, 10.9] | [30, 10.9] | Passed |
| generated control 2 | [90, 30.5] | [90, 30.5] | Passed |
| generated control 3 | [148, 60.0] | [148, 60.0] | Passed |
SHA-256 / 68ef6a3117055f401f3d944567da55ab04e7a4fd1db719b4eb1893be80df7422
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:31.221644+00:00.
Case digest / 1f2ce96ed8877b48f763480098c7d915a2b7d1c9b759f9dbcc7e089389792aba