The scan terminal service had grown into a 3.5k-line file that mixed endpoint orchestration, AI provider calls, fallback copy, JSON repair, and deterministic evidence guards. This extracts the city-AI helper layer into a focused module while preserving the old private names through imports for existing tests and callers.
Constraint: Keep behavior unchanged after the previous evidence-guard fixes and avoid a broad service rewrite.
Rejected: Split every scan-terminal concern at once | too much regression risk for this maintenance pass.
Confidence: high
Scope-risk: narrow
Tested: pytest tests/test_web_observability.py -q
Tested: npm run build