FA-71256 / Weather index computation / Open access
Aviation flight category: ceiling cover types · case 01
A scattered layer at 400 ft makes a clear day LIFR.
ROOT CAUSE
Scattered cloud is treated as a ceiling.
VERIFIED REPAIR
Only BKN, OVC and vertical visibility form a ceiling.
Unsuccessful approach: Dropping VV ignores an indefinite ceiling in fog.
Case contract
layers are [cover, height_ft]; the ceiling is the lowest BKN, OVC or VV layer (None if none). Ceiling class: None or >3000 VFR, 1000-3000 MVFR, 500-999 IFR, <500 LIFR. Visibility class: >5 VFR, 3-5 MVFR, 1 to <3 IFR, <1 LIFR. Report [ceiling, worse of the two classes].
Why this case matters
Pilots and dispatch use flight categories for go/no-go and alternate planning.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(vis_sm, layers):
ceil = None
for cover, h in layers:
if cover in ("SCT", "BKN", "OVC", "VV"):
if ceil is None or h < ceil:
ceil = h
def cls_c(c):
if c is None:
return 3
if c < 500:
return 0
if c < 1000:
return 1
if c <= 3000:
return 2
return 3
def cls_v(v):
if v < 1:
return 0
if v < 3:
return 1
if v <= 5:
return 2
return 3
names = ["LIFR", "IFR", "MVFR", "VFR"]
return [ceil, names[min(cls_c(ceil), cls_v(vis_sm))]]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[["vis 4 layers [['BKN', 400]]", [4, [['BKN', 400]]], [400, 'LIFR']], ["vis 5 layers [['FEW', 2500]]", [5, [['FEW', 2500]]], [None, 'MVFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 6 layers [['BKN', 3000]]", [6, [['BKN', 3000]]], [3000, 'MVFR']], ["vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]", [0.5, [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]], [1500, 'LIFR']], ["vis 2 layers [['OVC', 400], ['SCT', 5000], ['SCT', 200]]", [2, [['OVC', 400], ['SCT', 5000], ['SCT', 200]]], [400, 'LIFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']]], [["vis 3 layers [['BKN', 1000]]", [3, [['BKN', 1000]]], [1000, 'MVFR']], ["vis 3 layers [['SCT', 5000], ['FEW', 200]]", [3, [['SCT', 5000], ['FEW', 200]]], [None, 'MVFR']], ["vis 2 layers [['SCT', 5000]]", [2, [['SCT', 5000]]], [None, 'IFR']], ['vis 0.25 layers []', [0.25, []], [None, 'LIFR']], ["vis 6 layers [['BKN', 500], ['BKN', 5000]]", [6, [['BKN', 500], ['BKN', 5000]]], [500, 'IFR']], ["vis 5 layers [['BKN', 3100]]", [5, [['BKN', 3100]]], [3100, 'MVFR']], ["vis 0.5 layers [['SCT', 2500]]", [0.5, [['SCT', 2500]]], [None, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']]], [["vis 4 layers [['VV', 2500]]", [4, [['VV', 2500]]], [2500, 'MVFR']], ["vis 0.25 layers [['FEW', 5000]]", [0.25, [['FEW', 5000]]], [None, 'LIFR']], ["vis 1 layers [['OVC', 500]]", [1, [['OVC', 500]]], [500, 'IFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 10 layers [['FEW', 1500], ['OVC', 3000]]", [10, [['FEW', 1500], ['OVC', 3000]]], [3000, 'MVFR']], ['vis 1 layers []', [1, []], [None, 'IFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 3 layers [['SCT', 400]]", [3, [['SCT', 400]]], [None, 'MVFR']]], [["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 0.25 layers [['BKN', 400], ['BKN', 200]]", [0.25, [['BKN', 400], ['BKN', 200]]], [200, 'LIFR']], ["vis 10 layers [['SCT', 3100]]", [10, [['SCT', 3100]]], [None, 'VFR']], ['vis 1 layers []', [1, []], [None, 'IFR']], ['vis 10 layers []', [10, []], [None, 'VFR']], ["vis 5 layers [['BKN', 500]]", [5, [['BKN', 500]]], [500, 'IFR']], ["vis 2 layers [['FEW', 2500], ['FEW', 800]]", [2, [['FEW', 2500], ['FEW', 800]]], [None, 'IFR']], ["vis 4 layers [['SCT', 800], ['BKN', 3000]]", [4, [['SCT', 800], ['BKN', 3000]]], [3000, 'MVFR']]], [["vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]", [0.5, [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]], [1500, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 3 layers [['SCT', 400]]", [3, [['SCT', 400]]], [None, 'MVFR']], ["vis 1 layers [['VV', 2500]]", [1, [['VV', 2500]]], [2500, 'IFR']], ['vis 6 layers []', [6, []], [None, 'VFR']], ["vis 3 layers [['FEW', 500], ['SCT', 1000], ['OVC', 400]]", [3, [['FEW', 500], ['SCT', 1000], ['OVC', 400]]], [400, 'LIFR']], ["vis 3 layers [['BKN', 5000]]", [3, [['BKN', 5000]]], [5000, 'MVFR']], ["vis 3 layers [['FEW', 200]]", [3, [['FEW', 200]]], [None, 'MVFR']]]]
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 |
|---|---|---|---|
| vis 4 layers [['BKN', 400]] | [400, 'LIFR'] | [400, 'LIFR'] | Passed |
| vis 5 layers [['FEW', 2500]] | [None, 'MVFR'] | [None, 'MVFR'] | Passed |
| vis 0.5 layers [] | [None, 'LIFR'] | [None, 'LIFR'] | Passed |
| vis 6 layers [['BKN', 3000]] | [3000, 'MVFR'] | [3000, 'MVFR'] | Passed |
| vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]] | [800, 'LIFR'] | [1500, 'LIFR'] | Failed |
| vis 2 layers [['OVC', 400], ['SCT', 5000], ['SCT', 200]] | [200, 'LIFR'] | [400, 'LIFR'] | Failed |
| vis 0.5 layers [] | [None, 'LIFR'] | [None, 'LIFR'] | Passed |
| vis 0.5 layers [['FEW', 400], ['VV', 400]] | [400, 'LIFR'] | [400, 'LIFR'] | Passed |
SHA-256 / 32c2ac154b1741c2a3c894d2c15851e65ec36fcea2a48fc98c11cae188eb3384
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(vis_sm, layers):
ceil = None
for cover, h in layers:
if cover in ("BKN", "OVC"):
if ceil is None or h < ceil:
ceil = h
def cls_c(c):
if c is None:
return 3
if c < 500:
return 0
if c < 1000:
return 1
if c <= 3000:
return 2
return 3
def cls_v(v):
if v < 1:
return 0
if v < 3:
return 1
if v <= 5:
return 2
return 3
names = ["LIFR", "IFR", "MVFR", "VFR"]
return [ceil, names[min(cls_c(ceil), cls_v(vis_sm))]]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[["vis 4 layers [['BKN', 400]]", [4, [['BKN', 400]]], [400, 'LIFR']], ["vis 5 layers [['FEW', 2500]]", [5, [['FEW', 2500]]], [None, 'MVFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 6 layers [['BKN', 3000]]", [6, [['BKN', 3000]]], [3000, 'MVFR']], ["vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]", [0.5, [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]], [1500, 'LIFR']], ["vis 2 layers [['OVC', 400], ['SCT', 5000], ['SCT', 200]]", [2, [['OVC', 400], ['SCT', 5000], ['SCT', 200]]], [400, 'LIFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']]], [["vis 3 layers [['BKN', 1000]]", [3, [['BKN', 1000]]], [1000, 'MVFR']], ["vis 3 layers [['SCT', 5000], ['FEW', 200]]", [3, [['SCT', 5000], ['FEW', 200]]], [None, 'MVFR']], ["vis 2 layers [['SCT', 5000]]", [2, [['SCT', 5000]]], [None, 'IFR']], ['vis 0.25 layers []', [0.25, []], [None, 'LIFR']], ["vis 6 layers [['BKN', 500], ['BKN', 5000]]", [6, [['BKN', 500], ['BKN', 5000]]], [500, 'IFR']], ["vis 5 layers [['BKN', 3100]]", [5, [['BKN', 3100]]], [3100, 'MVFR']], ["vis 0.5 layers [['SCT', 2500]]", [0.5, [['SCT', 2500]]], [None, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']]], [["vis 4 layers [['VV', 2500]]", [4, [['VV', 2500]]], [2500, 'MVFR']], ["vis 0.25 layers [['FEW', 5000]]", [0.25, [['FEW', 5000]]], [None, 'LIFR']], ["vis 1 layers [['OVC', 500]]", [1, [['OVC', 500]]], [500, 'IFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 10 layers [['FEW', 1500], ['OVC', 3000]]", [10, [['FEW', 1500], ['OVC', 3000]]], [3000, 'MVFR']], ['vis 1 layers []', [1, []], [None, 'IFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 3 layers [['SCT', 400]]", [3, [['SCT', 400]]], [None, 'MVFR']]], [["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 0.25 layers [['BKN', 400], ['BKN', 200]]", [0.25, [['BKN', 400], ['BKN', 200]]], [200, 'LIFR']], ["vis 10 layers [['SCT', 3100]]", [10, [['SCT', 3100]]], [None, 'VFR']], ['vis 1 layers []', [1, []], [None, 'IFR']], ['vis 10 layers []', [10, []], [None, 'VFR']], ["vis 5 layers [['BKN', 500]]", [5, [['BKN', 500]]], [500, 'IFR']], ["vis 2 layers [['FEW', 2500], ['FEW', 800]]", [2, [['FEW', 2500], ['FEW', 800]]], [None, 'IFR']], ["vis 4 layers [['SCT', 800], ['BKN', 3000]]", [4, [['SCT', 800], ['BKN', 3000]]], [3000, 'MVFR']]], [["vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]", [0.5, [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]], [1500, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 3 layers [['SCT', 400]]", [3, [['SCT', 400]]], [None, 'MVFR']], ["vis 1 layers [['VV', 2500]]", [1, [['VV', 2500]]], [2500, 'IFR']], ['vis 6 layers []', [6, []], [None, 'VFR']], ["vis 3 layers [['FEW', 500], ['SCT', 1000], ['OVC', 400]]", [3, [['FEW', 500], ['SCT', 1000], ['OVC', 400]]], [400, 'LIFR']], ["vis 3 layers [['BKN', 5000]]", [3, [['BKN', 5000]]], [5000, 'MVFR']], ["vis 3 layers [['FEW', 200]]", [3, [['FEW', 200]]], [None, 'MVFR']]]]
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 |
|---|---|---|---|
| vis 4 layers [['BKN', 400]] | [400, 'LIFR'] | [400, 'LIFR'] | Passed |
| vis 5 layers [['FEW', 2500]] | [None, 'MVFR'] | [None, 'MVFR'] | Passed |
| vis 0.5 layers [] | [None, 'LIFR'] | [None, 'LIFR'] | Passed |
| vis 6 layers [['BKN', 3000]] | [3000, 'MVFR'] | [3000, 'MVFR'] | Passed |
| vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]] | [1500, 'LIFR'] | [1500, 'LIFR'] | Passed |
| vis 2 layers [['OVC', 400], ['SCT', 5000], ['SCT', 200]] | [400, 'LIFR'] | [400, 'LIFR'] | Passed |
| vis 0.5 layers [] | [None, 'LIFR'] | [None, 'LIFR'] | Passed |
| vis 0.5 layers [['FEW', 400], ['VV', 400]] | [None, 'LIFR'] | [400, 'LIFR'] | Failed |
SHA-256 / d79af58d030e647cde19a95bab947e32381ef2451fb3b9ab5b3ccdbd2ee0adbe
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(vis_sm, layers):
ceil = None
for cover, h in layers:
if cover in ("BKN", "OVC", "VV"):
if ceil is None or h < ceil:
ceil = h
def cls_c(c):
if c is None:
return 3
if c < 500:
return 0
if c < 1000:
return 1
if c <= 3000:
return 2
return 3
def cls_v(v):
if v < 1:
return 0
if v < 3:
return 1
if v <= 5:
return 2
return 3
names = ["LIFR", "IFR", "MVFR", "VFR"]
return [ceil, names[min(cls_c(ceil), cls_v(vis_sm))]]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[["vis 4 layers [['BKN', 400]]", [4, [['BKN', 400]]], [400, 'LIFR']], ["vis 5 layers [['FEW', 2500]]", [5, [['FEW', 2500]]], [None, 'MVFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 6 layers [['BKN', 3000]]", [6, [['BKN', 3000]]], [3000, 'MVFR']], ["vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]", [0.5, [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]], [1500, 'LIFR']], ["vis 2 layers [['OVC', 400], ['SCT', 5000], ['SCT', 200]]", [2, [['OVC', 400], ['SCT', 5000], ['SCT', 200]]], [400, 'LIFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']]], [["vis 3 layers [['BKN', 1000]]", [3, [['BKN', 1000]]], [1000, 'MVFR']], ["vis 3 layers [['SCT', 5000], ['FEW', 200]]", [3, [['SCT', 5000], ['FEW', 200]]], [None, 'MVFR']], ["vis 2 layers [['SCT', 5000]]", [2, [['SCT', 5000]]], [None, 'IFR']], ['vis 0.25 layers []', [0.25, []], [None, 'LIFR']], ["vis 6 layers [['BKN', 500], ['BKN', 5000]]", [6, [['BKN', 500], ['BKN', 5000]]], [500, 'IFR']], ["vis 5 layers [['BKN', 3100]]", [5, [['BKN', 3100]]], [3100, 'MVFR']], ["vis 0.5 layers [['SCT', 2500]]", [0.5, [['SCT', 2500]]], [None, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']]], [["vis 4 layers [['VV', 2500]]", [4, [['VV', 2500]]], [2500, 'MVFR']], ["vis 0.25 layers [['FEW', 5000]]", [0.25, [['FEW', 5000]]], [None, 'LIFR']], ["vis 1 layers [['OVC', 500]]", [1, [['OVC', 500]]], [500, 'IFR']], ['vis 0.5 layers []', [0.5, []], [None, 'LIFR']], ["vis 10 layers [['FEW', 1500], ['OVC', 3000]]", [10, [['FEW', 1500], ['OVC', 3000]]], [3000, 'MVFR']], ['vis 1 layers []', [1, []], [None, 'IFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 3 layers [['SCT', 400]]", [3, [['SCT', 400]]], [None, 'MVFR']]], [["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 0.25 layers [['BKN', 400], ['BKN', 200]]", [0.25, [['BKN', 400], ['BKN', 200]]], [200, 'LIFR']], ["vis 10 layers [['SCT', 3100]]", [10, [['SCT', 3100]]], [None, 'VFR']], ['vis 1 layers []', [1, []], [None, 'IFR']], ['vis 10 layers []', [10, []], [None, 'VFR']], ["vis 5 layers [['BKN', 500]]", [5, [['BKN', 500]]], [500, 'IFR']], ["vis 2 layers [['FEW', 2500], ['FEW', 800]]", [2, [['FEW', 2500], ['FEW', 800]]], [None, 'IFR']], ["vis 4 layers [['SCT', 800], ['BKN', 3000]]", [4, [['SCT', 800], ['BKN', 3000]]], [3000, 'MVFR']]], [["vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]", [0.5, [['SCT', 800], ['BKN', 1500], ['BKN', 2500]]], [1500, 'LIFR']], ["vis 0.5 layers [['FEW', 400], ['VV', 400]]", [0.5, [['FEW', 400], ['VV', 400]]], [400, 'LIFR']], ["vis 3 layers [['SCT', 400]]", [3, [['SCT', 400]]], [None, 'MVFR']], ["vis 1 layers [['VV', 2500]]", [1, [['VV', 2500]]], [2500, 'IFR']], ['vis 6 layers []', [6, []], [None, 'VFR']], ["vis 3 layers [['FEW', 500], ['SCT', 1000], ['OVC', 400]]", [3, [['FEW', 500], ['SCT', 1000], ['OVC', 400]]], [400, 'LIFR']], ["vis 3 layers [['BKN', 5000]]", [3, [['BKN', 5000]]], [5000, 'MVFR']], ["vis 3 layers [['FEW', 200]]", [3, [['FEW', 200]]], [None, 'MVFR']]]]
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 |
|---|---|---|---|
| vis 4 layers [['BKN', 400]] | [400, 'LIFR'] | [400, 'LIFR'] | Passed |
| vis 5 layers [['FEW', 2500]] | [None, 'MVFR'] | [None, 'MVFR'] | Passed |
| vis 0.5 layers [] | [None, 'LIFR'] | [None, 'LIFR'] | Passed |
| vis 6 layers [['BKN', 3000]] | [3000, 'MVFR'] | [3000, 'MVFR'] | Passed |
| vis 0.5 layers [['SCT', 800], ['BKN', 1500], ['BKN', 2500]] | [1500, 'LIFR'] | [1500, 'LIFR'] | Passed |
| vis 2 layers [['OVC', 400], ['SCT', 5000], ['SCT', 200]] | [400, 'LIFR'] | [400, 'LIFR'] | Passed |
| vis 0.5 layers [] | [None, 'LIFR'] | [None, 'LIFR'] | Passed |
| vis 0.5 layers [['FEW', 400], ['VV', 400]] | [400, 'LIFR'] | [400, 'LIFR'] | Passed |
SHA-256 / de0c15dfac6619a50b9fa3000b5ee93472e85595ac899489cf42c33f1ae20a38
Verification & scope
Stipulated deterministic teaching model of an operational weather index; coefficients and thresholds are fixed by the contract and no claim of standards conformance is made. 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:48:28.019919+00:00.
Case digest / c2fe7273cd08d173bdd09f39fad2be43904f351bcc96a75fb2e206ae6a51eb1b