Where: faircode/manifest.py's RowFilter dataclass (around lines 24-44).
The gap: unlike its sibling dataclasses TargetSpec and ProtectedAttribute (both validate their required companion fields in __post_init__), RowFilter has no __post_init__ at all - a filter block with column set but no operator (isin/not_isin/equals/not_equals/notna) silently keeps every row instead of erroring.
Repro:
$ python3 -c "
import pandas as pd
from faircode.manifest import RowFilter
df = pd.DataFrame({'race': ['White','Black','Asian'], 'outcome':[1,0,1]})
rf = RowFilter(column='race')
result = rf.apply(df)
print(len(result), len(df))
"
3 3
No error, no warning - the filter silently no-ops and every row passes through.
Why it matters: faircode/MANIFEST_SPEC.md explicitly documents "exactly one of the operators below" as required for a row_filters entry, but the code silently accepts zero. A blanked/copy-pasted YAML entry, or a forgotten operator in a new audit's manifest, would silently include rows a manifest author intended to exclude - with no indication anything went wrong.
Suggested fix: add a __post_init__ to RowFilter requiring at least one of isin/not_isin/equals/not_equals/notna to be set, matching the validation style already used in TargetSpec/ProtectedAttribute.
Where:
faircode/manifest.py'sRowFilterdataclass (around lines 24-44).The gap: unlike its sibling dataclasses
TargetSpecandProtectedAttribute(both validate their required companion fields in__post_init__),RowFilterhas no__post_init__at all - a filter block withcolumnset but no operator (isin/not_isin/equals/not_equals/notna) silently keeps every row instead of erroring.Repro:
No error, no warning - the filter silently no-ops and every row passes through.
Why it matters:
faircode/MANIFEST_SPEC.mdexplicitly documents "exactly one of the operators below" as required for arow_filtersentry, but the code silently accepts zero. A blanked/copy-pasted YAML entry, or a forgotten operator in a new audit's manifest, would silently include rows a manifest author intended to exclude - with no indication anything went wrong.Suggested fix: add a
__post_init__toRowFilterrequiring at least one ofisin/not_isin/equals/not_equals/notnato be set, matching the validation style already used inTargetSpec/ProtectedAttribute.