# US-026 — Smart model assignment via demand×coverage scoring Status: done Priority: Medium Stage: Implemented ## Context `/v1/network/assign` currently picks the model with the largest uncovered shard gap, ignoring traffic. A model serving 1000 RPM at 60% coverage is far more valuable to fill than a zero-traffic model at 50% coverage. ## Scoring formula ``` score = (demand_rpm + 1.0) × (coverage_deficit + 0.01) ``` - `demand_rpm`: combined RPM from `_StatsCollector.get_combined_stats()` - `coverage_deficit`: fraction of model layers with zero node coverage, in [0.0, 1.0] - `+1.0` floor: models with no traffic still compete by coverage - `+0.01` floor: fully-covered models still have a non-zero score if they have traffic `price_per_token: 0.0` reserved in the response for future billing integration. ## Acceptance criteria - [ ] `_handle_network_assign` computes score per model and returns the highest - [ ] Demand uses combined stats (local + peer slices) - [ ] `price_per_token: 0.0` present in response - [ ] Test: high-demand low-coverage model beats low-demand high-coverage model - [ ] `python -m pytest` passes