FA-11906 / Scientific pipeline provenance / Open access
A provenance fingerprint erases processing order · case 01
A provenance fingerprint erases processing order.
ROOT CAUSE
Stage names are treated as an unordered set.
VERIFIED REPAIR
Preserve the full ordered sequence including repeated stages.
Unsuccessful approach: Preserving first occurrence loses intentional repeated passes.
Case contract
Return the pipeline stage sequence unchanged as the symbolic provenance fingerprint; order and multiplicity matter.
Why this case matters
A deterministic offline model of scientific workflow bookkeeping; the fixtures test provenance contracts without modeling instruments or biological inference.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(stages):
return sorted(set(stages))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
s='wash'+str(N)
check('repeat wash after extraction', solve([s,'extract',s]), [s,'extract',s])
check('order preserved', solve(['normalize','filter']), ['normalize','filter'])
check('empty process', solve([]), [])
check('one stage', solve([s]), [s])
check('consecutive passes', solve([s,s]), [s,s])
check('reverse sequence', solve(['z','a','z','b']), ['z','a','z','b'])
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 |
|---|---|---|---|
| repeat wash after extraction | ['extract', 'wash1'] | ['wash1', 'extract', 'wash1'] | Failed |
| order preserved | ['filter', 'normalize'] | ['normalize', 'filter'] | Failed |
| empty process | [] | [] | Passed |
| one stage | ['wash1'] | ['wash1'] | Passed |
| consecutive passes | ['wash1'] | ['wash1', 'wash1'] | Failed |
| reverse sequence | ['a', 'b', 'z'] | ['z', 'a', 'z', 'b'] | Failed |
SHA-256 / 53a14d93ce23e9565d66177fceb23f3527c9f9ac6ca4c54dc727c415187bfca4
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(stages):
return list(dict.fromkeys(stages))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
s='wash'+str(N)
check('repeat wash after extraction', solve([s,'extract',s]), [s,'extract',s])
check('order preserved', solve(['normalize','filter']), ['normalize','filter'])
check('empty process', solve([]), [])
check('one stage', solve([s]), [s])
check('consecutive passes', solve([s,s]), [s,s])
check('reverse sequence', solve(['z','a','z','b']), ['z','a','z','b'])
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 |
|---|---|---|---|
| repeat wash after extraction | ['wash1', 'extract'] | ['wash1', 'extract', 'wash1'] | Failed |
| order preserved | ['normalize', 'filter'] | ['normalize', 'filter'] | Passed |
| empty process | [] | [] | Passed |
| one stage | ['wash1'] | ['wash1'] | Passed |
| consecutive passes | ['wash1'] | ['wash1', 'wash1'] | Failed |
| reverse sequence | ['z', 'a', 'b'] | ['z', 'a', 'z', 'b'] | Failed |
SHA-256 / 0b69ac11c7b2289f620da9914bce8175538bd10beb83e7b00e890e22747c1de1
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(stages):
return list(stages)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
s='wash'+str(N)
check('repeat wash after extraction', solve([s,'extract',s]), [s,'extract',s])
check('order preserved', solve(['normalize','filter']), ['normalize','filter'])
check('empty process', solve([]), [])
check('one stage', solve([s]), [s])
check('consecutive passes', solve([s,s]), [s,s])
check('reverse sequence', solve(['z','a','z','b']), ['z','a','z','b'])
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 |
|---|---|---|---|
| repeat wash after extraction | ['wash1', 'extract', 'wash1'] | ['wash1', 'extract', 'wash1'] | Passed |
| order preserved | ['normalize', 'filter'] | ['normalize', 'filter'] | Passed |
| empty process | [] | [] | Passed |
| one stage | ['wash1'] | ['wash1'] | Passed |
| consecutive passes | ['wash1', 'wash1'] | ['wash1', 'wash1'] | Passed |
| reverse sequence | ['z', 'a', 'z', 'b'] | ['z', 'a', 'z', 'b'] | Passed |
SHA-256 / 38666f5aabba8f5061cb5da1fe8c38947b802c9f1fc983314af18c16030e8287
Verification & scope
In-memory symbolic records only; no instrument, assay, or production workflow validation. 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:52.101256+00:00.
Case digest / d5a612d4065634a8ae4be04be74011066842360dd7686c1f88c298a8797998c6