Summary
While working on #90 (add __all__ to detection/__init__.py), I found that several files in detection/ currently fail to even parse on main, because multiple feature branches were merged without fully resolving the resulting code conflicts (the merge left both versions concatenated together instead of picking one).
detection/model_inference.py — critical, blocks RiskScorer
RiskScorer.score() is the worst case: score(), score_continuous(), and score_continuous_batch() (added across at least 4 different commits/branches — BFT consensus voting, weighted ensemble calibration, meta-learning adapter blending, and batch scoring) are interleaved into one corrupted block. The def score(...) signature itself appears to be missing, and the body references variables (scores_100, final_score, diverged, X) that aren't defined in scope. self.weights is read in score() but never assigned in __init__.
$ python -c "import detection.model_inference"
File "detection/model_inference.py", line 282
"""Continuous ensemble scores for a batch of feature rows.
^^^^^^^^^^
SyntaxError: invalid syntax
This means RiskScorer is currently unimportable on main, and so is anything that depends on it (detection.forensic_report, detection.causal_attribution, detection.audit_trail, scripts/score_wallet.py, etc).
tests/test_model_inference.py is comprehensive enough to fully pin down the intended behavior of the BFT-voting and weighted-calibration paths, so it should be usable as the spec for reconstructing this method correctly — but it's a real risk-scoring logic change for a fraud-detection pipeline, so it deserves a dedicated, carefully-reviewed PR rather than a drive-by fix.
detection/forensic_report.py — duplicate dead classes + duplicate kwargs
Two full (different) definitions of TradeEvidence and ForensicReport are concatenated back-to-back (the later one — with causal_attribution/propagation_path/shap_explanations — is the one actually used elsewhere, e.g. detection/audit_trail.py). ForensicReportGenerator.generate() also has duplicate keyword-only parameters and MAX_EVIDENCE_TRADES is defined twice:
$ python -c "import detection.forensic_report"
File "detection/forensic_report.py", line 301
risk_score_dict: dict | None = None,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: duplicate argument 'risk_score_dict' in function definition
The generate() method body also has a second, broken copy of the trade-evidence/SHAP/Benford assembly logic appended after the working one, referencing undefined names (shap_explanations, risk_score). This part is safe to delete outright — it's dead code shadowed by the working implementation, and the template/tests confirm the "most anomalous trades" (_select_anomalous_trades) implementation is the intended one, not the leftover "sort by amount" duplicate.
Lower-severity duplicate-definition issues (mypy [no-redef], not blocking import)
config.py: GNN_EMBEDDING_DIM defined 3x, GNN_HIDDEN_DIM defined 2x (same values, harmless but should be deduplicated).
detection/wallet_graph.py: two different build_co_trade_graph functions with different signatures (window_hours: int vs window_hours: float = 24.0) and different bodies — the second silently shadows the first.
detection/feature_engineering.py: compute_graph_embedding_features defined twice (line 277 and 732), and Any is used at line 247 without being imported from typing.
Suggested next step
Given the scope (reconciling several competing feature implementations), I'd suggest splitting this into at least two follow-up PRs: one for model_inference.py (highest risk, needs careful validation against tests/test_model_inference.py), and one for the lower-risk cleanup in forensic_report.py/config.py/wallet_graph.py/feature_engineering.py.
Summary
While working on #90 (add
__all__todetection/__init__.py), I found that several files indetection/currently fail to even parse onmain, because multiple feature branches were merged without fully resolving the resulting code conflicts (the merge left both versions concatenated together instead of picking one).detection/model_inference.py— critical, blocksRiskScorerRiskScorer.score()is the worst case:score(),score_continuous(), andscore_continuous_batch()(added across at least 4 different commits/branches — BFT consensus voting, weighted ensemble calibration, meta-learning adapter blending, and batch scoring) are interleaved into one corrupted block. Thedef score(...)signature itself appears to be missing, and the body references variables (scores_100,final_score,diverged,X) that aren't defined in scope.self.weightsis read inscore()but never assigned in__init__.This means
RiskScoreris currently unimportable onmain, and so is anything that depends on it (detection.forensic_report,detection.causal_attribution,detection.audit_trail,scripts/score_wallet.py, etc).tests/test_model_inference.pyis comprehensive enough to fully pin down the intended behavior of the BFT-voting and weighted-calibration paths, so it should be usable as the spec for reconstructing this method correctly — but it's a real risk-scoring logic change for a fraud-detection pipeline, so it deserves a dedicated, carefully-reviewed PR rather than a drive-by fix.detection/forensic_report.py— duplicate dead classes + duplicate kwargsTwo full (different) definitions of
TradeEvidenceandForensicReportare concatenated back-to-back (the later one — withcausal_attribution/propagation_path/shap_explanations— is the one actually used elsewhere, e.g.detection/audit_trail.py).ForensicReportGenerator.generate()also has duplicate keyword-only parameters andMAX_EVIDENCE_TRADESis defined twice:The
generate()method body also has a second, broken copy of the trade-evidence/SHAP/Benford assembly logic appended after the working one, referencing undefined names (shap_explanations,risk_score). This part is safe to delete outright — it's dead code shadowed by the working implementation, and the template/tests confirm the "most anomalous trades" (_select_anomalous_trades) implementation is the intended one, not the leftover "sort by amount" duplicate.Lower-severity duplicate-definition issues (mypy
[no-redef], not blocking import)config.py:GNN_EMBEDDING_DIMdefined 3x,GNN_HIDDEN_DIMdefined 2x (same values, harmless but should be deduplicated).detection/wallet_graph.py: two differentbuild_co_trade_graphfunctions with different signatures (window_hours: intvswindow_hours: float = 24.0) and different bodies — the second silently shadows the first.detection/feature_engineering.py:compute_graph_embedding_featuresdefined twice (line 277 and 732), andAnyis used at line 247 without being imported fromtyping.Suggested next step
Given the scope (reconciling several competing feature implementations), I'd suggest splitting this into at least two follow-up PRs: one for
model_inference.py(highest risk, needs careful validation againsttests/test_model_inference.py), and one for the lower-risk cleanup inforensic_report.py/config.py/wallet_graph.py/feature_engineering.py.