FA-60861 / Bond day-count conventions / Open access
30/360 US bond basis: the D1=31 clamp is made conditional on the end date · case 01
A start on the 31st with an end mid-month produces a count one day short.
ROOT CAUSE
The D1 clamp was written as a mirror of the D2 rule and only fires when D2 is also 30 or later.
VERIFIED REPAIR
Clamp D1=31 to 30 unconditionally after the other rules.
Unsuccessful approach: Gating the clamp on D2 not being the 1st still leaves most mid-month end dates unadjusted.
Case contract
Inputs are two [year, month, day] dates. Apply, in order: if both dates are the last day of February, D2 becomes 30; if D1 is the last day of February, D1 becomes 30; if D2 is 31 and (adjusted) D1 is 30 or 31, D2 becomes 30; if D1 is 31, D1 becomes 30. Return 360*(Y2-Y1)+30*(M2-M1)+(D2-D1) as an integer day count.
Why this case matters
Bond accrual and pricing systems depend on exact day-count arithmetic; a single-day error changes settlement cash.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(a, b):
y1, m1, d1 = a
y2, m2, d2 = b
def feb_end(y, m, d):
leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
return m == 2 and d == (29 if leap else 28)
e1 = feb_end(y1, m1, d1)
e2 = feb_end(y2, m2, d2)
if e1 and e2:
d2 = 30
if e1:
d1 = 30
if d2 == 31 and d1 >= 30:
d2 = 30
if d1 == 31 and d2 >= 30:
d1 = 30
return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression start day clamp 1', [[2050, 12, 31], [2052, 2, 29]], 419], ['regression start day clamp 2', [[2041, 10, 31], [2044, 2, 29]], 839], ['partial repair probe 1', [[2025, 7, 31], [2025, 1, 31]], -180], ['partial repair probe 2', [[2015, 1, 31], [2017, 3, 31]], 780], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2014, 11, 24], [2015, 5, 10]], 166], ['normal control 2', [[2050, 7, 15], [2051, 2, 28]], 223]], [['regression start day clamp 1', [[2038, 1, 31], [2038, 5, 24]], 114], ['regression start day clamp 2', [[2054, 10, 31], [2056, 1, 14]], 434], ['partial repair probe 1', [[2035, 1, 31], [2035, 12, 31]], 330], ['partial repair probe 2', [[2009, 10, 31], [2012, 3, 31]], 870], ['boundary control 1', [[2024, 1, 1], [2024, 1, 1]], 0], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2000, 2, 29], [2000, 4, 19]], 49], ['normal control 2', [[2074, 6, 28], [2075, 11, 30]], 512]], [['regression start day clamp 1', [[2018, 10, 31], [2019, 6, 6]], 216], ['regression start day clamp 2', [[2094, 5, 31], [2095, 2, 28]], 268], ['partial repair probe 1', [[2053, 7, 31], [2056, 1, 31]], 900], ['partial repair probe 2', [[2090, 1, 31], [2090, 7, 31]], 180], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2023, 5, 30], [2023, 7, 31]], 60], ['normal control 1', [[2058, 4, 28], [2059, 11, 11]], 553], ['normal control 2', [[2014, 6, 11], [2017, 9, 7]], 1166]], [['regression start day clamp 1', [[2096, 5, 31], [2097, 5, 1]], 331], ['regression start day clamp 2', [[2075, 1, 31], [2076, 5, 13]], 463], ['partial repair probe 1', [[2047, 8, 31], [2050, 6, 30]], 1020], ['partial repair probe 2', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 1', [[2024, 1, 1], [2024, 1, 1]], 0], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2071, 11, 18], [2074, 12, 31]], 1123], ['normal control 2', [[2089, 4, 29], [2090, 3, 31]], 332]], [['regression start day clamp 1', [[2017, 10, 31], [2018, 3, 2]], 122], ['regression start day clamp 2', [[2086, 5, 31], [2088, 2, 29]], 629], ['partial repair probe 1', [[2029, 1, 31], [2032, 1, 31]], 1080], ['partial repair probe 2', [[2042, 1, 31], [2043, 12, 31]], 690], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2004, 4, 30], [2005, 7, 30]], 450], ['normal control 2', [[2020, 2, 29], [2022, 2, 28]], 720]]]
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 |
|---|---|---|---|
| regression start day clamp 1 | 418 | 419 | Failed |
| regression start day clamp 2 | 838 | 839 | Failed |
| partial repair probe 1 | -180 | -180 | Passed |
| partial repair probe 2 | 780 | 780 | Passed |
| boundary control 1 | 180 | 180 | Passed |
| boundary control 2 | 180 | 180 | Passed |
| normal control 1 | 166 | 166 | Passed |
| normal control 2 | 223 | 223 | Passed |
SHA-256 / c0e605cc9aa6e4a3b4e52b0917934882bba96a6b1f6b3b3347985fe1bb867451
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(a, b):
y1, m1, d1 = a
y2, m2, d2 = b
def feb_end(y, m, d):
leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
return m == 2 and d == (29 if leap else 28)
e1 = feb_end(y1, m1, d1)
e2 = feb_end(y2, m2, d2)
if e1 and e2:
d2 = 30
if e1:
d1 = 30
if d2 == 31 and d1 >= 30:
d2 = 30
if d1 == 31 and d2 == 1:
d1 = 30
return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression start day clamp 1', [[2050, 12, 31], [2052, 2, 29]], 419], ['regression start day clamp 2', [[2041, 10, 31], [2044, 2, 29]], 839], ['partial repair probe 1', [[2025, 7, 31], [2025, 1, 31]], -180], ['partial repair probe 2', [[2015, 1, 31], [2017, 3, 31]], 780], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2014, 11, 24], [2015, 5, 10]], 166], ['normal control 2', [[2050, 7, 15], [2051, 2, 28]], 223]], [['regression start day clamp 1', [[2038, 1, 31], [2038, 5, 24]], 114], ['regression start day clamp 2', [[2054, 10, 31], [2056, 1, 14]], 434], ['partial repair probe 1', [[2035, 1, 31], [2035, 12, 31]], 330], ['partial repair probe 2', [[2009, 10, 31], [2012, 3, 31]], 870], ['boundary control 1', [[2024, 1, 1], [2024, 1, 1]], 0], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2000, 2, 29], [2000, 4, 19]], 49], ['normal control 2', [[2074, 6, 28], [2075, 11, 30]], 512]], [['regression start day clamp 1', [[2018, 10, 31], [2019, 6, 6]], 216], ['regression start day clamp 2', [[2094, 5, 31], [2095, 2, 28]], 268], ['partial repair probe 1', [[2053, 7, 31], [2056, 1, 31]], 900], ['partial repair probe 2', [[2090, 1, 31], [2090, 7, 31]], 180], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2023, 5, 30], [2023, 7, 31]], 60], ['normal control 1', [[2058, 4, 28], [2059, 11, 11]], 553], ['normal control 2', [[2014, 6, 11], [2017, 9, 7]], 1166]], [['regression start day clamp 1', [[2096, 5, 31], [2097, 5, 1]], 331], ['regression start day clamp 2', [[2075, 1, 31], [2076, 5, 13]], 463], ['partial repair probe 1', [[2047, 8, 31], [2050, 6, 30]], 1020], ['partial repair probe 2', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 1', [[2024, 1, 1], [2024, 1, 1]], 0], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2071, 11, 18], [2074, 12, 31]], 1123], ['normal control 2', [[2089, 4, 29], [2090, 3, 31]], 332]], [['regression start day clamp 1', [[2017, 10, 31], [2018, 3, 2]], 122], ['regression start day clamp 2', [[2086, 5, 31], [2088, 2, 29]], 629], ['partial repair probe 1', [[2029, 1, 31], [2032, 1, 31]], 1080], ['partial repair probe 2', [[2042, 1, 31], [2043, 12, 31]], 690], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2004, 4, 30], [2005, 7, 30]], 450], ['normal control 2', [[2020, 2, 29], [2022, 2, 28]], 720]]]
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 |
|---|---|---|---|
| regression start day clamp 1 | 418 | 419 | Failed |
| regression start day clamp 2 | 838 | 839 | Failed |
| partial repair probe 1 | -181 | -180 | Failed |
| partial repair probe 2 | 779 | 780 | Failed |
| boundary control 1 | 180 | 180 | Passed |
| boundary control 2 | 180 | 180 | Passed |
| normal control 1 | 166 | 166 | Passed |
| normal control 2 | 223 | 223 | Passed |
SHA-256 / 513cfbe24c150cd72b8f1d631f74615bc7597bfff7b3f8d5222ee16e4ececcae
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(a, b):
y1, m1, d1 = a
y2, m2, d2 = b
def feb_end(y, m, d):
leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
return m == 2 and d == (29 if leap else 28)
e1 = feb_end(y1, m1, d1)
e2 = feb_end(y2, m2, d2)
if e1 and e2:
d2 = 30
if e1:
d1 = 30
if d2 == 31 and d1 >= 30:
d2 = 30
if d1 == 31:
d1 = 30
return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression start day clamp 1', [[2050, 12, 31], [2052, 2, 29]], 419], ['regression start day clamp 2', [[2041, 10, 31], [2044, 2, 29]], 839], ['partial repair probe 1', [[2025, 7, 31], [2025, 1, 31]], -180], ['partial repair probe 2', [[2015, 1, 31], [2017, 3, 31]], 780], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2014, 11, 24], [2015, 5, 10]], 166], ['normal control 2', [[2050, 7, 15], [2051, 2, 28]], 223]], [['regression start day clamp 1', [[2038, 1, 31], [2038, 5, 24]], 114], ['regression start day clamp 2', [[2054, 10, 31], [2056, 1, 14]], 434], ['partial repair probe 1', [[2035, 1, 31], [2035, 12, 31]], 330], ['partial repair probe 2', [[2009, 10, 31], [2012, 3, 31]], 870], ['boundary control 1', [[2024, 1, 1], [2024, 1, 1]], 0], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2000, 2, 29], [2000, 4, 19]], 49], ['normal control 2', [[2074, 6, 28], [2075, 11, 30]], 512]], [['regression start day clamp 1', [[2018, 10, 31], [2019, 6, 6]], 216], ['regression start day clamp 2', [[2094, 5, 31], [2095, 2, 28]], 268], ['partial repair probe 1', [[2053, 7, 31], [2056, 1, 31]], 900], ['partial repair probe 2', [[2090, 1, 31], [2090, 7, 31]], 180], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2023, 5, 30], [2023, 7, 31]], 60], ['normal control 1', [[2058, 4, 28], [2059, 11, 11]], 553], ['normal control 2', [[2014, 6, 11], [2017, 9, 7]], 1166]], [['regression start day clamp 1', [[2096, 5, 31], [2097, 5, 1]], 331], ['regression start day clamp 2', [[2075, 1, 31], [2076, 5, 13]], 463], ['partial repair probe 1', [[2047, 8, 31], [2050, 6, 30]], 1020], ['partial repair probe 2', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 1', [[2024, 1, 1], [2024, 1, 1]], 0], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2071, 11, 18], [2074, 12, 31]], 1123], ['normal control 2', [[2089, 4, 29], [2090, 3, 31]], 332]], [['regression start day clamp 1', [[2017, 10, 31], [2018, 3, 2]], 122], ['regression start day clamp 2', [[2086, 5, 31], [2088, 2, 29]], 629], ['partial repair probe 1', [[2029, 1, 31], [2032, 1, 31]], 1080], ['partial repair probe 2', [[2042, 1, 31], [2043, 12, 31]], 690], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2004, 4, 30], [2005, 7, 30]], 450], ['normal control 2', [[2020, 2, 29], [2022, 2, 28]], 720]]]
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 |
|---|---|---|---|
| regression start day clamp 1 | 419 | 419 | Passed |
| regression start day clamp 2 | 839 | 839 | Passed |
| partial repair probe 1 | -180 | -180 | Passed |
| partial repair probe 2 | 780 | 780 | Passed |
| boundary control 1 | 180 | 180 | Passed |
| boundary control 2 | 180 | 180 | Passed |
| normal control 1 | 166 | 166 | Passed |
| normal control 2 | 223 | 223 | Passed |
SHA-256 / 155e5b46df8336ba1abcc2ce7e0559665c94c2aaf1f1c5e4f70a91428526180a
Verification & scope
A deterministic toy contract stated explicitly in the contract field; no claim of conformance to any published convention text. 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:46:49.602973+00:00.
Case digest / fe9107cdb16760ab399ecd326d37956099837398e7d1338b73f38eb1def42429