Where: faircode/MANIFEST_SPEC.md:157 vs faircode/strategies.py's predict_in_processing/predict_post_processing.
The gap: the spec states: "S3 and S4 need the protected attribute at both fit and predict time as sensitive_features." This is true for S4 (post_processing, ThresholdOptimizer) but not S3 (in_processing, ExponentiatedGradient) - predict_in_processing never passes sensitive_features to mitigator.predict(), and it can't: ExponentiatedGradient.predict doesn't accept that parameter at all.
Repro:
>>> from fairlearn.reductions import ExponentiatedGradient
>>> import inspect
>>> inspect.signature(ExponentiatedGradient.predict)
<Signature (self, X, random_state=None)>
>>> from fairlearn.postprocessing import ThresholdOptimizer
>>> inspect.signature(ThresholdOptimizer.predict)
<Signature (self, X, *, sensitive_features, random_state=None)>
Matches the actual code: predict_in_processing(mitigator, X_test, random_state) takes no sensitive-attribute argument, while predict_post_processing(optimizer, X_test, sensitive_test, random_state) requires one.
Why it matters: a contributor reading MANIFEST_SPEC.md to understand or extend S3 (or to debug why S3's predict path doesn't take per-row group info) would be misled into thinking that's an oversight in the code rather than the actual, documented-elsewhere design (S3's own docstring in strategies.py correctly describes fit-time-only sensitive-feature use).
Suggested fix: reword MANIFEST_SPEC.md line 157 to scope the predict-time requirement to S4 only, e.g. "S4 needs the protected attribute at both fit and predict time; S3 needs it only at fit time (ExponentiatedGradient.predict takes no sensitive_features argument)."
Where:
faircode/MANIFEST_SPEC.md:157vsfaircode/strategies.py'spredict_in_processing/predict_post_processing.The gap: the spec states: "S3 and S4 need the protected attribute at both fit and predict time as
sensitive_features." This is true for S4 (post_processing,ThresholdOptimizer) but not S3 (in_processing,ExponentiatedGradient) -predict_in_processingnever passessensitive_featurestomitigator.predict(), and it can't:ExponentiatedGradient.predictdoesn't accept that parameter at all.Repro:
Matches the actual code:
predict_in_processing(mitigator, X_test, random_state)takes no sensitive-attribute argument, whilepredict_post_processing(optimizer, X_test, sensitive_test, random_state)requires one.Why it matters: a contributor reading
MANIFEST_SPEC.mdto understand or extend S3 (or to debug why S3's predict path doesn't take per-row group info) would be misled into thinking that's an oversight in the code rather than the actual, documented-elsewhere design (S3's own docstring instrategies.pycorrectly describes fit-time-only sensitive-feature use).Suggested fix: reword
MANIFEST_SPEC.mdline 157 to scope the predict-time requirement to S4 only, e.g. "S4 needs the protected attribute at both fit and predict time; S3 needs it only at fit time (ExponentiatedGradient.predicttakes nosensitive_featuresargument)."