"""Auto leak-detection framework (Phase 0 foundation).
Concrete detectors register here in later phases:
from .registry import registry
from .my_detector import MyDetector
registry.register(MyDetector())
Public API:
run_all_detectors(company_id, detector_ids=None) -> summary dict
registry — global DetectorRegistry singleton
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from .base import BaseDetector, LeakCandidate # noqa: F401
from .registry import DetectorRegistry, registry, get_detector_settings, SETTINGS_KEY # noqa: F401
# ---------------------------------------------------------------------------
# Detector registration (Phase 1 — quick wins)
# ---------------------------------------------------------------------------
from .qb_overdue import QbOverdueDetector # noqa: E402
from .qb_draft import QbDraftDetector # noqa: E402
from .crm_stale import CrmStaleDealDetector # noqa: E402
from .crm_slipped import CrmSlippedCloseDetector # noqa: E402
from .crm_orphaned import CrmOrphanedDealDetector # noqa: E402
from .ads_zero_conv import AdsZeroConversionDetector # noqa: E402
from .leads_unworked import LeadsUnworkedDetector # noqa: E402
# ---------------------------------------------------------------------------
# Detector registration (Phase 2 — behavioral patterns)
# ---------------------------------------------------------------------------
from .phase2_detectors import DETECTORS as PHASE2_DETECTORS # noqa: E402
# ---------------------------------------------------------------------------
# Detector registration (Phase 3 — cross-connector intelligence)
# ---------------------------------------------------------------------------
from .phase3_detectors import DETECTORS as PHASE3_DETECTORS # noqa: E402
from .speed_to_lead import SpeedToLeadDetector # noqa: E402
# ---------------------------------------------------------------------------
# Detector registration (P1 — remodeling sector / backlog + change orders)
# ---------------------------------------------------------------------------
from .backlog_burndown import BacklogBurndownDetector # noqa: E402
from .change_order_leak import ChangeOrderLeakDetector # noqa: E402
# ---------------------------------------------------------------------------
# Detector registration (P3 — project slippage + margin tracking)
# ---------------------------------------------------------------------------
from .project_schedule_variance import ProjectScheduleVarianceDetector # noqa: E402
from .project_margin_leak import ProjectMarginLeakDetector # noqa: E402
# ---------------------------------------------------------------------------
# Detector registration (P4 — multi-location rollup analytics)
# ---------------------------------------------------------------------------
from .multi_location_rollup import MultiLocationRollupDetector # noqa: E402
DETECTORS = [
QbOverdueDetector,
QbDraftDetector,
CrmStaleDealDetector,
CrmSlippedCloseDetector,
CrmOrphanedDealDetector,
AdsZeroConversionDetector,
LeadsUnworkedDetector,
*PHASE2_DETECTORS,
SpeedToLeadDetector,
*PHASE3_DETECTORS,
BacklogBurndownDetector,
ChangeOrderLeakDetector,
ProjectScheduleVarianceDetector,
ProjectMarginLeakDetector,
MultiLocationRollupDetector,
]
for _detector_cls in DETECTORS:
if registry.get(_detector_cls.id) is None:
registry.register(_detector_cls())
def run_all_detectors(
company_id: str,
detector_ids: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""Run all enabled leak detectors for a company. Must be called inside
an app context."""
return registry.run_all(company_id, detector_ids=detector_ids)