Merge branch 'ralph/dashboard-realtime' — SSE realtime + story rollback
Server-Sent Events (TODO P3):
- GET /api/ralph/stream — signature-based change detection (poll FS 2s, emit
doar la diff), heartbeat 30s, X-Accel-Buffering:no
- HTTPServer → ThreadingHTTPServer (altfel SSE blochează toate endpoint-urile)
- ralph.html: EventSource cu fallback permanent la polling 5s când CLOSED.
Badge: 🟢 Live / ⏱ Polling / Offline
Story rollback (TODO P3):
- POST /api/ralph/<slug>/rollback — git revert --no-edit HEAD; fallback
git reset --hard HEAD~1 doar la conflict
- Decrementează passes pe ultima story complete; clears failed/blocked/retries
(atomic temp+rename)
- Slug strict regex ^[A-Za-z0-9_-]{1,64}$ + reject path traversal explicit
- Buton ↩️ pe card-uri running; confirm dialog înainte de execuție
- Response: {success, message, reverted_commit, story_reverted, method}
Tests: 39/39 pe test_dashboard_ralph_endpoint (era 19; +20 cazuri noi).
# Conflicts:
# dashboard/api.py
# dashboard/handlers/ralph.py