FAILURE MAP
← Case archive

FA-9246 / Packaging / Open access

Artifact identity tuple: Debug and release artifacts collide · case 01

Debug and release artifacts collide.

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

ROOT CAUSE

The implementation substitutes None, for flavor,, so debug and release artifacts collide.

VERIFIED REPAIR

Preserve the requested build flavor.

Unsuccessful approach: The attempted repair substitutes 'release',. Fixture 1 still yields ('my-pkg', (1, 2, 3), 'cp311', 'release', None) instead of ('my-pkg', (1, 2, 3), 'cp311', 'debug', None).

Case contract

An artifact identity consists of project, version tuple, target ABI, build flavor, and optional classifier; preserve all fields including empty classifier.

Why this case matters

An offline model of artifact identity tuple, suitable for testing build and release tooling without external services.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(project, version, abi, flavor, classifier):
    return (project.casefold().replace('_', '-'), tuple(version), abi, None, classifier)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('fixture 1', solve('My_Pkg', (1, 2, 3), 'cp311', 'debug', None), ('my-pkg', (1, 2, 3), 'cp311', 'debug', None))
check('fixture 2', solve('my-pkg', (1, 2, 3), 'cp312', 'release', 'tests'), ('my-pkg', (1, 2, 3), 'cp312', 'release', 'tests'))
check('fixture 3', solve('A_B', (2, 0, 1), 'abi3', 'release', ''), ('a-b', (2, 0, 1), 'abi3', 'release', ''))
check('fixture 4', solve('A_B', (2, 0, 2), 'abi3', 'debug', 'docs'), ('a-b', (2, 0, 2), 'abi3', 'debug', 'docs'))
check('fixture 5', solve('a_b', (2, 1, 0), 'none', 'release', None), ('a-b', (2, 1, 0), 'none', 'release', None))
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
fixture 1['my-pkg', [1, 2, 3], 'cp311', None, None]['my-pkg', [1, 2, 3], 'cp311', 'debug', None]Failed
fixture 2['my-pkg', [1, 2, 3], 'cp312', None, 'tests']['my-pkg', [1, 2, 3], 'cp312', 'release', 'tests']Failed
fixture 3['a-b', [2, 0, 1], 'abi3', None, '']['a-b', [2, 0, 1], 'abi3', 'release', '']Failed
fixture 4['a-b', [2, 0, 2], 'abi3', None, 'docs']['a-b', [2, 0, 2], 'abi3', 'debug', 'docs']Failed
fixture 5['a-b', [2, 1, 0], 'none', None, None]['a-b', [2, 1, 0], 'none', 'release', None]Failed

SHA-256 / 54b4473b3712234df4edca3e781136123edbc72bb6e1df2b90e087bec259b976

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(project, version, abi, flavor, classifier):
    return (project.casefold().replace('_', '-'), tuple(version), abi, 'release', classifier)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('fixture 1', solve('My_Pkg', (1, 2, 3), 'cp311', 'debug', None), ('my-pkg', (1, 2, 3), 'cp311', 'debug', None))
check('fixture 2', solve('my-pkg', (1, 2, 3), 'cp312', 'release', 'tests'), ('my-pkg', (1, 2, 3), 'cp312', 'release', 'tests'))
check('fixture 3', solve('A_B', (2, 0, 1), 'abi3', 'release', ''), ('a-b', (2, 0, 1), 'abi3', 'release', ''))
check('fixture 4', solve('A_B', (2, 0, 2), 'abi3', 'debug', 'docs'), ('a-b', (2, 0, 2), 'abi3', 'debug', 'docs'))
check('fixture 5', solve('a_b', (2, 1, 0), 'none', 'release', None), ('a-b', (2, 1, 0), 'none', 'release', None))
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
fixture 1['my-pkg', [1, 2, 3], 'cp311', 'release', None]['my-pkg', [1, 2, 3], 'cp311', 'debug', None]Failed
fixture 2['my-pkg', [1, 2, 3], 'cp312', 'release', 'tests']['my-pkg', [1, 2, 3], 'cp312', 'release', 'tests']Passed
fixture 3['a-b', [2, 0, 1], 'abi3', 'release', '']['a-b', [2, 0, 1], 'abi3', 'release', '']Passed
fixture 4['a-b', [2, 0, 2], 'abi3', 'release', 'docs']['a-b', [2, 0, 2], 'abi3', 'debug', 'docs']Failed
fixture 5['a-b', [2, 1, 0], 'none', 'release', None]['a-b', [2, 1, 0], 'none', 'release', None]Passed

SHA-256 / 5f291e35bca478d8a88c36be88aecb59e33091b9e0e9d24a38a47fb97860d8ac

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(project, version, abi, flavor, classifier):
    return (project.casefold().replace('_', '-'), tuple(version), abi, flavor, classifier)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('fixture 1', solve('My_Pkg', (1, 2, 3), 'cp311', 'debug', None), ('my-pkg', (1, 2, 3), 'cp311', 'debug', None))
check('fixture 2', solve('my-pkg', (1, 2, 3), 'cp312', 'release', 'tests'), ('my-pkg', (1, 2, 3), 'cp312', 'release', 'tests'))
check('fixture 3', solve('A_B', (2, 0, 1), 'abi3', 'release', ''), ('a-b', (2, 0, 1), 'abi3', 'release', ''))
check('fixture 4', solve('A_B', (2, 0, 2), 'abi3', 'debug', 'docs'), ('a-b', (2, 0, 2), 'abi3', 'debug', 'docs'))
check('fixture 5', solve('a_b', (2, 1, 0), 'none', 'release', None), ('a-b', (2, 1, 0), 'none', 'release', None))
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
fixture 1['my-pkg', [1, 2, 3], 'cp311', 'debug', None]['my-pkg', [1, 2, 3], 'cp311', 'debug', None]Passed
fixture 2['my-pkg', [1, 2, 3], 'cp312', 'release', 'tests']['my-pkg', [1, 2, 3], 'cp312', 'release', 'tests']Passed
fixture 3['a-b', [2, 0, 1], 'abi3', 'release', '']['a-b', [2, 0, 1], 'abi3', 'release', '']Passed
fixture 4['a-b', [2, 0, 2], 'abi3', 'debug', 'docs']['a-b', [2, 0, 2], 'abi3', 'debug', 'docs']Passed
fixture 5['a-b', [2, 1, 0], 'none', 'release', None]['a-b', [2, 1, 0], 'none', 'release', None]Passed

SHA-256 / a9d760254e3b00fc0366719694bb81c8e0a76be3b875189dfb452decd83d14bd

Verification & scope

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

Case digest / 3c30c5965545a697d3ab35802735d636e52a94e9a6f09bc34ac738fbce663c08