GraFOSa Classical ML Proposal
Classical machine learning for graph-first structural analysis — no GPU, no LLM, fully interpretable.
Why Classical ML
| Property | Classical ML | LLM/GenAI |
|---|---|---|
| Training time | Seconds to minutes | Hours to days |
| Inference time | Microseconds | Seconds |
| Hardware | Any CPU | GPU / large RAM |
| Interpretability | Full (feature importance, decision paths) | Black box |
| Data needed | Hundreds of samples | Millions of tokens |
| Reproducibility | Deterministic | Stochastic |
| Cost | Zero | API costs or GPU hours |
Classical ML answers the question: given the graph metrics we already compute, what can we PREDICT?
Proposal: 7 ML Capabilities
1. Bug Prediction (Random Forest / XGBoost)
Problem: Which modules are most likely to have bugs in the next sprint?
Approach: Train a gradient-boosted classifier on graph-derived features per node.
Features from GraFOSa's existing graph:
- Fan-in (incoming dependencies)
- Fan-out (outgoing dependencies)
- Degree centrality (from PageRank, already computed)
- Cycle membership (from
find_cycles()) - Depth in dependency tree
- Number of cross-service edges
- Health score delta over last N snapshots
- File age (from git history)
- Churn rate (commits/month from git ground truth)
- Number of contributors (ownership spread)
- Lines of code (from scanner)
- Test coverage signal (has corresponding test file?)
Labels: Git ground truth — files that appeared in bug-fix commits (already available via GitGroundTruth.extract()).
Output: Per-file bug probability (0-1) with feature importance explaining WHY.
Expected performance: F1 0.80-0.90 based on published research (Microsoft, Google, Huawei studies on defect prediction from software metrics).
Dependencies: scikit-learn (or zero-dep implementation with decision stumps)
Fits in GraFOSa: Runs after health_report(), uses the same graph kernel. Results stored as node properties (_bug_probability, _bug_risk_factors).
2. Anomaly Detection (Isolation Forest)
Problem: Something changed in the codebase health and it's unusual — but what?
Approach: Isolation Forest on multivariate health metrics time series. Detects outlier health snapshots without manual thresholds.
Features (per health snapshot):
- Overall score
- Per-section scores (architecture, dependencies, connectivity, etc.)
- Issue counts by severity
- Score velocity (rate of change)
- Number of new issues since last snapshot
How it works: Isolation Forest isolates anomalies by randomly splitting features. Anomalies need fewer splits to isolate (they're "different" from the crowd). No need to define "what's normal" — the algorithm learns it.
Output:
- Anomaly score per snapshot (-1 = anomaly, +1 = normal)
- Which dimensions contributed most to the anomaly
- Pairs naturally with the existing
HealthTrendengine
Use case: "Your health score dropped 3 points — is that normal churn or an anomaly?" Isolation Forest answers this without hand-tuned thresholds.
Dependencies: scikit-learn or pure-Python implementation (~100 lines)
3. Changepoint Detection (PELT / CUSUM)
Problem: When EXACTLY did the architecture start degrading?
Approach: PELT (Pruned Exact Linear Time) algorithm on health score time series. Finds points where the statistical properties of the series change.
What it detects:
- Mean shifts: "health was ~85 for 3 months, then dropped to ~72"
- Variance shifts: "scores used to be stable, now they swing wildly"
- Trend breaks: "was improving, then started declining"
Output:
- List of changepoint timestamps
- Confidence interval for each changepoint
- Before/after statistics (mean, variance)
- Correlation with git events (which commit coincides with the changepoint?)
Why this matters: Trend lines smooth over abrupt changes. Changepoint detection finds the EXACT moment something broke — then git correlation identifies the likely cause.
Dependencies: ruptures library or pure-Python CUSUM (~50 lines)
4. Survival Analysis (Cox Proportional Hazards)
Problem: How long until this module needs a fix?
Approach: Cox model treats each module as a "patient" and bug-fix commits as "events." Graph metrics are covariates that influence the hazard rate.
Covariates:
- Fan-in / fan-out ratio
- Centrality score
- Number of cross-service dependencies
- Age since last modification
- Ownership concentration (bus factor)
- Cyclomatic complexity proxy (from graph structure)
Output:
- Hazard ratio per feature: "each additional dependency increases bug risk by 12%"
- Survival curves per module: "auth.py has 70% chance of needing a fix within 30 days"
- Risk groups: high/medium/low risk cohorts
Why this is powerful: Unlike bug prediction (binary yes/no), survival analysis gives a TIME estimate. Combined with GraFOSa's health trending, you get "this module will need attention in ~3 weeks."
Dependencies: lifelines library or manual Breslow estimator
5. Service Boundary Discovery (Spectral Clustering / DBSCAN)
Problem: Are the current microservice boundaries optimal, or should things be reorganized?
Approach: Cluster nodes in the dependency graph based on connectivity patterns. Nodes that are densely connected internally but sparsely connected externally form natural service boundaries.
Methods:
- Spectral clustering: Uses the graph Laplacian eigenvalues. Finds k clusters that minimize edge cuts. Good when you know how many services you want.
- DBSCAN: Density-based clustering. Finds clusters of arbitrary shape without specifying k. Good for discovering natural boundaries.
- Modularity optimization: Already partially available via
modularity_communities()in GraphKernel.
Input: The adjacency matrix from GraphKernel (already available via _outgoing/_incoming indices).
Output:
- Proposed service boundaries (sets of files/symbols)
- Boundary quality score (modularity coefficient)
- Cross-boundary edge count (coupling metric)
- Comparison with current service assignments
- Migration cost estimate (how many files would move)
Why this matters: Manual service boundary decisions are often historical accidents. Clustering reveals what the code itself thinks the boundaries should be.
6. Co-change Mining (Association Rules / Apriori)
Problem: Which files are secretly coupled — always changed together but not connected in the dependency graph?
Approach: Mine git commit history for frequent co-change patterns using Apriori or FP-Growth algorithms.
Data source: Git commits — each commit is a "transaction," each changed file is an "item."
Output:
- Association rules:
{auth.py, gateway.py} → {middleware.py}(confidence 87%, support 23 commits) - Logical coupling matrix: files that co-change but have no dependency edge = hidden coupling
- Surprising rules: high confidence + no graph edge = architecture smell
Metrics per rule:
- Support: How often the pattern occurs
- Confidence: Given A changed, how often does B also change
- Lift: Is the co-change more frequent than random chance
Why this matters: The dependency graph shows STRUCTURAL coupling. Co-change mining shows LOGICAL coupling — the coupling humans create through behavior. The gap between these two is where architectural debt lives.
Dependencies: Pure Python implementation (~150 lines) or mlxtend
7. Scoring Config Optimization (Bayesian Optimization)
Problem: GraFOSa's health scoring has dozens of weights and thresholds. Are they optimal?
Approach: Use Bayesian optimization to find the scoring configuration that best predicts real-world outcomes (bugs, incidents, developer pain).
Search space:
- Section weights (architecture, dependencies, connectivity, etc.)
- Severity penalties (critical, error, warning)
- Grade thresholds (green, amber, red)
- God-object thresholds (LOC limits)
- Cycle penalty weights
Objective function: Maximize correlation between GraFOSa health score and actual bug frequency (from git ground truth). A good config should assign low scores to modules that later have bugs.
Method: Gaussian Process-based Bayesian optimization. Builds a surrogate model of the objective function, efficiently explores the parameter space. Typically converges in 50-100 evaluations.
Output:
- Optimized config parameters
- Sensitivity analysis: which parameters matter most
- Confidence intervals on optimal values
- Before/after comparison on historical data
Why this matters: Currently, GraFOSa's scoring weights are hand-tuned. This closes the loop — let the data tell you what matters.
Dependencies: scikit-optimize or pure-Python GP implementation
Integration Architecture
GraFOSa Graph Kernel
│
┌─────────────┼─────────────────┐
│ │ │
Graph Metrics Git History Health Snapshots
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌────────────┐ ┌──────────────┐
│ Bug Predict │ │ Co-change │ │ Anomaly Det │
│ (XGBoost) │ │ (Apriori) │ │ (IsoForest) │
├─────────────┤ ├────────────┤ ├──────────────┤
│ Survival │ │ Boundary │ │ Changepoint │
│ (Cox Model) │ │ (Spectral) │ │ (PELT) │
└──────┬──────┘ └─────┬──────┘ └──────┬───────┘
│ │ │
└───────────────┼────────────────┘
│
Bayesian Optimization
(tunes GraFOSa config)
│
▼
Predictions as Node Properties
(_bug_prob, _survival_days, etc.)Proposed Implementation
File Structure
src/grafosa/ml/
├── __init__.py
├── bug_predictor.py # Random Forest / XGBoost bug prediction
├── anomaly_detector.py # Isolation Forest on health time series
├── changepoint.py # PELT / CUSUM changepoint detection
├── survival.py # Cox proportional hazards model
├── clustering.py # Spectral clustering + DBSCAN for service boundaries
├── cochange.py # Association rule mining from git history
├── config_optimizer.py # Bayesian optimization for scoring config
└── features.py # Shared feature extraction from graph kernelMCP Tools
| Tool | Purpose |
|---|---|
ml_bug_predict | Predict bug probability per module |
ml_anomaly_detect | Detect anomalies in health history |
ml_changepoint | Find when health characteristics changed |
ml_survival | Estimate time-to-next-fix per module |
ml_cluster_services | Discover optimal service boundaries |
ml_cochange | Mine hidden coupling from git history |
ml_optimize_config | Auto-tune scoring configuration |
ml_feature_importance | Show which graph metrics drive predictions |
Dependency Strategy
Zero-dep core (pure Python):
- Changepoint detection (CUSUM)
- Co-change mining (Apriori)
- Feature extraction
- Basic anomaly detection (z-score based)
Optional scikit-learn (recommended):
- Random Forest, Isolation Forest, Spectral Clustering
pip install scikit-learn(~30MB)
Optional extras:
xgboostfor gradient boosting (better bug prediction)lifelinesfor survival analysisscikit-optimizefor Bayesian optimization
All modules degrade gracefully — if scikit-learn isn't installed, fall back to pure-Python implementations with reduced accuracy.
Priority Order
| Priority | Capability | Value | Effort |
|---|---|---|---|
| 1 | Bug Prediction | Highest — directly actionable | Medium |
| 2 | Co-change Mining | High — reveals hidden coupling | Low |
| 3 | Anomaly Detection | High — pairs with health trending | Low |
| 4 | Service Boundary Discovery | High — architectural insights | Medium |
| 5 | Changepoint Detection | Medium — root cause analysis | Low |
| 6 | Scoring Config Optimization | Medium — self-improving system | Medium |
| 7 | Survival Analysis | Medium — time-based predictions | Medium |
Recommended start: Bug Prediction + Co-change Mining + Anomaly Detection — three capabilities, highest combined value, lowest combined effort. All three feed off data GraFOSa already computes (graph metrics, git history, health snapshots).
Expected Impact
- Bug prediction catches ~85% of future bugs before they happen
- Co-change mining exposes hidden coupling the dependency graph misses
- Anomaly detection replaces manual threshold tuning with learned baselines
- Service clustering provides data-driven microservice boundary recommendations
- Config optimization closes the loop — GraFOSa tunes itself
All of this runs on a laptop, trains in seconds, needs zero GPU, and every prediction is fully explainable.