FAILURE MAP
← Case archive

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

Post-wash swatch gauge: row gauge dimension · case 01

Body length is off by the width-to-height difference of the swatch.

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

ROOT CAUSE

Row gauge divides by the swatch width instead of its height.

VERIFIED REPAIR

Divide rows by post-wash height.

Unsuccessful approach: Pre-wash height ignores vertical relaxation.

Case contract

A swatch of sts x rows measured before and after washing. Use post-wash dimensions: cast on = half-up target_w*sts/w_post; rows = target_h*rows/h_post rounded to the nearest even number (ties up); width change % = (w_post/w_pre - 1)*100 half-up to 0.1. Return [cast_on, rows, width_change].

Why this case matters

Knitters block swatches because many fibres grow or shrink after washing.

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(sts, rows, w_pre, w_post, h_pre, h_post, target_w, target_h):
    wpo = Fraction(w_post)
    hpo = Fraction(h_post)
    cast = math.floor(target_w * sts / wpo + Fraction(1, 2))
    rr = target_h * rows / wpo
    row_count = 2 * math.floor(rr / 2 + Fraction(1, 2))
    change = (wpo / Fraction(w_pre) - 1) * 100
    return [cast, row_count, math.floor(change * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['regression: row gauge dimension', [24, 40, '10.5', '10.5', '10.2', '10.4', 50, 45], [114, 174, 0.0]],
  ['repair check: row gauge dimension', [24, 40, '10', '11', '10', '10.4', 50, 30], [109, 116, 10.0]],
  ['generated control 1', [24, 28, '10.5', '10.5', '10.2', '10', 55, 60], [126, 168, 0.0]],
  ['generated control 2', [20, 32, '10', '10', '10', '9', 50, 30], [100, 106, 0.0]],
  ['generated control 3', [20, 30, '10', '10', '10', '9.5', 50, 30], [100, 94, 0.0]]],
 [['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['regression: row gauge dimension', [20, 28, '10', '10', '10', '10.4', 50, 30], [100, 80, 0.0]],
  ['repair check: row gauge dimension', [20, 32, '10', '11', '10', '9', 50, 60], [91, 214, 10.0]],
  ['generated control 1', [20, 28, '10.5', '10', '10', '10', 40, 60], [80, 168, -4.8]],
  ['generated control 2', [20, 30, '10', '10', '10.2', '10.4', 50, 45], [100, 130, 0.0]],
  ['generated control 3', [20, 32, '10.5', '10.8', '10', '10', 55, 60], [102, 192, 2.9]]],
 [['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['regression: row gauge dimension', [22, 30, '10.5', '10', '10', '10.4', 55, 60], [121, 174, -4.8]],
  ['repair check: row gauge dimension', [30, 40, '10.5', '9.5', '10.2', '9.5', 50, 60], [158, 252, -9.5]],
  ['generated control 1', [20, 32, '10', '10.5', '10', '9.5', 50, 30], [95, 102, 5.0]],
  ['generated control 2', [24, 30, '10', '10', '10', '9.5', 40, 30], [96, 94, 0.0]],
  ['generated control 3', [22, 28, '10', '11', '10', '9', 40, 60], [80, 186, 10.0]]],
 [['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['regression: row gauge dimension', [24, 40, '10.5', '9.5', '10.2', '9', 40, 45], [101, 200, -9.5]],
  ['repair check: row gauge dimension', [22, 40, '10', '10', '10', '9.5', 40, 60], [88, 252, 0.0]],
  ['generated control 1', [22, 28, '10', '11', '10', '10', 50, 30], [100, 84, 10.0]],
  ['generated control 2', [24, 30, '10', '10.5', '10.2', '9', 40, 60], [91, 200, 5.0]],
  ['generated control 3', [30, 40, '10.5', '10', '10.2', '10', 50, 30], [150, 120, -4.8]]],
 [['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['regression: row gauge dimension', [24, 30, '10', '10.8', '10.2', '10.4', 50, 30], [111, 86, 8.0]],
  ['repair check: row gauge dimension', [30, 30, '10.5', '10.8', '10', '9.5', 50, 30], [139, 94, 2.9]],
  ['generated control 1', [24, 28, '10.5', '10', '10', '10', 50, 60], [120, 168, -4.8]],
  ['generated control 2', [20, 32, '10', '10.5', '10.2', '9', 40, 60], [76, 214, 5.0]],
  ['generated control 3', [20, 32, '10', '10', '10', '10.4', 55, 60], [110, 184, 0.0]]]]
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
no change[100, 126, 0.0][100, 126, 0.0]Passed
growth[100, 164, 10.0][100, 174, 10.0]Failed
shrink[101, 102, -9.5][101, 106, -9.5]Failed
regression: row gauge dimension[114, 172, 0.0][114, 174, 0.0]Failed
repair check: row gauge dimension[109, 110, 10.0][109, 116, 10.0]Failed
generated control 1[126, 160, 0.0][126, 168, 0.0]Failed
generated control 2[100, 96, 0.0][100, 106, 0.0]Failed
generated control 3[100, 90, 0.0][100, 94, 0.0]Failed

SHA-256 / dcb81b6fe2235844439f71cace661ad63a2432ed855f8c04abbf097dd24396df

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(sts, rows, w_pre, w_post, h_pre, h_post, target_w, target_h):
    wpo = Fraction(w_post)
    hpo = Fraction(h_post)
    cast = math.floor(target_w * sts / wpo + Fraction(1, 2))
    rr = target_h * rows / Fraction(h_pre)
    row_count = 2 * math.floor(rr / 2 + Fraction(1, 2))
    change = (wpo / Fraction(w_pre) - 1) * 100
    return [cast, row_count, math.floor(change * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['regression: row gauge dimension', [24, 40, '10.5', '10.5', '10.2', '10.4', 50, 45], [114, 174, 0.0]],
  ['repair check: row gauge dimension', [24, 40, '10', '11', '10', '10.4', 50, 30], [109, 116, 10.0]],
  ['generated control 1', [24, 28, '10.5', '10.5', '10.2', '10', 55, 60], [126, 168, 0.0]],
  ['generated control 2', [20, 32, '10', '10', '10', '9', 50, 30], [100, 106, 0.0]],
  ['generated control 3', [20, 30, '10', '10', '10', '9.5', 50, 30], [100, 94, 0.0]]],
 [['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['regression: row gauge dimension', [20, 28, '10', '10', '10', '10.4', 50, 30], [100, 80, 0.0]],
  ['repair check: row gauge dimension', [20, 32, '10', '11', '10', '9', 50, 60], [91, 214, 10.0]],
  ['generated control 1', [20, 28, '10.5', '10', '10', '10', 40, 60], [80, 168, -4.8]],
  ['generated control 2', [20, 30, '10', '10', '10.2', '10.4', 50, 45], [100, 130, 0.0]],
  ['generated control 3', [20, 32, '10.5', '10.8', '10', '10', 55, 60], [102, 192, 2.9]]],
 [['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['regression: row gauge dimension', [22, 30, '10.5', '10', '10', '10.4', 55, 60], [121, 174, -4.8]],
  ['repair check: row gauge dimension', [30, 40, '10.5', '9.5', '10.2', '9.5', 50, 60], [158, 252, -9.5]],
  ['generated control 1', [20, 32, '10', '10.5', '10', '9.5', 50, 30], [95, 102, 5.0]],
  ['generated control 2', [24, 30, '10', '10', '10', '9.5', 40, 30], [96, 94, 0.0]],
  ['generated control 3', [22, 28, '10', '11', '10', '9', 40, 60], [80, 186, 10.0]]],
 [['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['regression: row gauge dimension', [24, 40, '10.5', '9.5', '10.2', '9', 40, 45], [101, 200, -9.5]],
  ['repair check: row gauge dimension', [22, 40, '10', '10', '10', '9.5', 40, 60], [88, 252, 0.0]],
  ['generated control 1', [22, 28, '10', '11', '10', '10', 50, 30], [100, 84, 10.0]],
  ['generated control 2', [24, 30, '10', '10.5', '10.2', '9', 40, 60], [91, 200, 5.0]],
  ['generated control 3', [30, 40, '10.5', '10', '10.2', '10', 50, 30], [150, 120, -4.8]]],
 [['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['regression: row gauge dimension', [24, 30, '10', '10.8', '10.2', '10.4', 50, 30], [111, 86, 8.0]],
  ['repair check: row gauge dimension', [30, 30, '10.5', '10.8', '10', '9.5', 50, 30], [139, 94, 2.9]],
  ['generated control 1', [24, 28, '10.5', '10', '10', '10', 50, 60], [120, 168, -4.8]],
  ['generated control 2', [20, 32, '10', '10.5', '10.2', '9', 40, 60], [76, 214, 5.0]],
  ['generated control 3', [20, 32, '10', '10', '10', '10.4', 55, 60], [110, 184, 0.0]]]]
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
no change[100, 126, 0.0][100, 126, 0.0]Passed
growth[100, 180, 10.0][100, 174, 10.0]Failed
shrink[101, 94, -9.5][101, 106, -9.5]Failed
regression: row gauge dimension[114, 176, 0.0][114, 174, 0.0]Failed
repair check: row gauge dimension[109, 120, 10.0][109, 116, 10.0]Failed
generated control 1[126, 164, 0.0][126, 168, 0.0]Failed
generated control 2[100, 96, 0.0][100, 106, 0.0]Failed
generated control 3[100, 90, 0.0][100, 94, 0.0]Failed

SHA-256 / 9e88c7e0a0565a89b56eb9b2c26f61a17b0df48eed0e08779c4a036adfefaa99

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(sts, rows, w_pre, w_post, h_pre, h_post, target_w, target_h):
    wpo = Fraction(w_post)
    hpo = Fraction(h_post)
    cast = math.floor(target_w * sts / wpo + Fraction(1, 2))
    rr = target_h * rows / hpo
    row_count = 2 * math.floor(rr / 2 + Fraction(1, 2))
    change = (wpo / Fraction(w_pre) - 1) * 100
    return [cast, row_count, math.floor(change * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['regression: row gauge dimension', [24, 40, '10.5', '10.5', '10.2', '10.4', 50, 45], [114, 174, 0.0]],
  ['repair check: row gauge dimension', [24, 40, '10', '11', '10', '10.4', 50, 30], [109, 116, 10.0]],
  ['generated control 1', [24, 28, '10.5', '10.5', '10.2', '10', 55, 60], [126, 168, 0.0]],
  ['generated control 2', [20, 32, '10', '10', '10', '9', 50, 30], [100, 106, 0.0]],
  ['generated control 3', [20, 30, '10', '10', '10', '9.5', 50, 30], [100, 94, 0.0]]],
 [['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['regression: row gauge dimension', [20, 28, '10', '10', '10', '10.4', 50, 30], [100, 80, 0.0]],
  ['repair check: row gauge dimension', [20, 32, '10', '11', '10', '9', 50, 60], [91, 214, 10.0]],
  ['generated control 1', [20, 28, '10.5', '10', '10', '10', 40, 60], [80, 168, -4.8]],
  ['generated control 2', [20, 30, '10', '10', '10.2', '10.4', 50, 45], [100, 130, 0.0]],
  ['generated control 3', [20, 32, '10.5', '10.8', '10', '10', 55, 60], [102, 192, 2.9]]],
 [['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['regression: row gauge dimension', [22, 30, '10.5', '10', '10', '10.4', 55, 60], [121, 174, -4.8]],
  ['repair check: row gauge dimension', [30, 40, '10.5', '9.5', '10.2', '9.5', 50, 60], [158, 252, -9.5]],
  ['generated control 1', [20, 32, '10', '10.5', '10', '9.5', 50, 30], [95, 102, 5.0]],
  ['generated control 2', [24, 30, '10', '10', '10', '9.5', 40, 30], [96, 94, 0.0]],
  ['generated control 3', [22, 28, '10', '11', '10', '9', 40, 60], [80, 186, 10.0]]],
 [['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['regression: row gauge dimension', [24, 40, '10.5', '9.5', '10.2', '9', 40, 45], [101, 200, -9.5]],
  ['repair check: row gauge dimension', [22, 40, '10', '10', '10', '9.5', 40, 60], [88, 252, 0.0]],
  ['generated control 1', [22, 28, '10', '11', '10', '10', 50, 30], [100, 84, 10.0]],
  ['generated control 2', [24, 30, '10', '10.5', '10.2', '9', 40, 60], [91, 200, 5.0]],
  ['generated control 3', [30, 40, '10.5', '10', '10.2', '10', 50, 30], [150, 120, -4.8]]],
 [['growth', [22, 30, '10', '11', '10', '10.4', 50, 60], [100, 174, 10.0]],
  ['shrink', [24, 32, '10.5', '9.5', '10.2', '9', 40, 30], [101, 106, -9.5]],
  ['no change', [20, 28, '10', '10', '10', '10', 50, 45], [100, 126, 0.0]],
  ['regression: row gauge dimension', [24, 30, '10', '10.8', '10.2', '10.4', 50, 30], [111, 86, 8.0]],
  ['repair check: row gauge dimension', [30, 30, '10.5', '10.8', '10', '9.5', 50, 30], [139, 94, 2.9]],
  ['generated control 1', [24, 28, '10.5', '10', '10', '10', 50, 60], [120, 168, -4.8]],
  ['generated control 2', [20, 32, '10', '10.5', '10.2', '9', 40, 60], [76, 214, 5.0]],
  ['generated control 3', [20, 32, '10', '10', '10', '10.4', 55, 60], [110, 184, 0.0]]]]
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
no change[100, 126, 0.0][100, 126, 0.0]Passed
growth[100, 174, 10.0][100, 174, 10.0]Passed
shrink[101, 106, -9.5][101, 106, -9.5]Passed
regression: row gauge dimension[114, 174, 0.0][114, 174, 0.0]Passed
repair check: row gauge dimension[109, 116, 10.0][109, 116, 10.0]Passed
generated control 1[126, 168, 0.0][126, 168, 0.0]Passed
generated control 2[100, 106, 0.0][100, 106, 0.0]Passed
generated control 3[100, 94, 0.0][100, 94, 0.0]Passed

SHA-256 / 4b26b666a8be2fa9486b81e57b82bdace952a8f8620ff7192185b4a51343edf4

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

Case digest / 33c5d95bed81dab7160ceb95001b0b1b499dec6f4c387f89fd35df556758f901