Introduce app/services/run_log_service.py — the single crash-proof write seam
for all deploy-path logging (plan 51 D4/D5/D6/D10). It batches writes (one DB
commit + one socket emit per flush instead of commit-per-line), keeps a truthful
80-line in-memory failure tail, records per-step timings, sanitizes ANSI/\r,
caps persisted rows at 5000, and matches a plain-language failure hint. The
DeploymentPlanRunner now funnels log/step/close through it.
Add DockerService.compose_up_streaming (Popen + --ansi never --progress plain,
line callback) so template installs stream the real pull/build transcript live
and a failure persists a meaningful tail — the biggest reason install errors
were unreadable. The runner's local compose.up step adopts it.
Replace the orphaned build_* socket surface (zero subscribers, grep-verified)
with the Deploy Console channel: emit_deploy_log / emit_deploy_status to room
deploy_{job_id}, plus subscribe_deploy/unsubscribe_deploy (authenticated).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>