Plan: Wire A2A Bridge to Zed LSP Integration (Final)

Connect the A2A bridge to LSP server with automatic service discovery, graceful fallback, SOPS-based auth, workspace-specific Zed configuration, cached semantic queries with WebSocket progressive enhancement, persistent self-healing connections, custom hover notifications with standard LSP fallback, debounced requests with cancellation, Redis connection pooling, 2s SPARQL timeout, conditional WebSocket auth, cache-busting on file saves, system PATH installation with auto-detection, and health check polling.

Notes / Best Practices Alignment

Acceptance Criteria

Testing Checklist

Rollout / Feature Flags

Success Metrics (Initial Targets)

Measurement Notes

Steps

  1. Standardize A2A config (non-local by default) — Replace hard-coded localhost with A2A_BASE_URL everywhere; default to http://localhost:8001 only when unset for dev. Update Zed_Setup.md example URLs, add A2A_BASE_URL to .env.example (AC: 1)

  2. Add A2A and Redis services in docker-compose.skeleton.yml — Add redis service (port 6379, maxmemory-policy allkeys-lru) and a2a-service container (port 8001) with depends_on: [oxigraph, redis] health conditions, interval: 10s, timeout: 3s, retries: 3, keep LSP HTTP client timeout at 5s. Ensure REDIS_URL is configurable and not assumed to be local (AC: 1, 7)

  3. Configure SOPS secrets with explicit auth mode — Add A2A_BEARER_TOKEN, A2A_BASE_URL, REDIS_URL, ENVIRONMENT, and A2A_AUTH_MODE=required|optional|disabled to .secrets.env.sops; update apps/sea-lsp/src/providers/hover.rs to read from std::env::var(), enforce bearer token when A2A_AUTH_MODE=required, and allow unauthenticated only when A2A_AUTH_MODE=optional|disabled in dev (AC: 2)

  4. Generate dual Zed settings with gitignore in ui-setup.sh — Create ~/.config/zed/settings.json (global) and .zed/settings.json (workspace-specific) using Python JSON merge, add .zed/settings.json to .gitignore, commit .zed/settings.json.example

  5. Add smart LSP build with auto-detect install in justfile — Create build-lsp (mtime check on apps/sea-lsp/src/**/*.rs, cargo build --release -p sea-lsp) and install-lsp (detect ~/.local/bin/ vs bin write permissions, update shell config PATH), invoked by setup-zed

  6. Implement persistent self-healing WebSocket in LSP in apps/sea-lsp/src/providers/hover.rs — Create WebSocketManager with tokio-tungstenite, maintain connection to {A2A_BASE_URL}/ws, exponential backoff reconnection (1s→2s→5s→10s max), send bearer token in handshake when A2A_AUTH_MODE=required|optional, use A2A task protocol from schemas/a2a/task.schema.json, and log reconnect attempts (AC: 4)

  7. Add debounced hover with request cancellation in apps/sea-lsp/src/providers/hover.rs — Implement 200ms debounce using tokio::time::sleep, store pending task handle in Arc<Mutex<Option<JoinHandle>>>, abort previous task on new hover request, prevents SPARQL query flood during rapid cursor movement. Track dropped/aborted counts for observability (AC: 5)

  8. Add Redis connection pooling to A2A service in services/a2a/src/config/redis.py — Create redis.asyncio.ConnectionPool with max_connections=20, connection timeout 5s, use pool in FastAPI lifespan, inject into hover route handlers. If REDIS_URL is unset and ENVIRONMENT=development, fall back to an in-memory cache implementation (AC: 3)

  9. Implement cached semantic enrichment with 2s timeout in services/a2a/src/routes/editor_actions.py — Add cache (TTL 5min, keys hover:{file_hash}:{position}:v1), return immediate cached response, de-duplicate in-flight queries per key, and use asyncio.wait_for(query_oxigraph(), timeout=2.0) to prevent blocking. Publish via WebSocket using A2A output artifact schema and track cache hit/miss (AC: 5)

  10. Add custom notification with standard LSP fallback in backend.rs — Send custom sea/hoverRefresh notification when enriched context arrives via WebSocket, detect Zed support via client.capabilities.experimental.sea_hover_refresh, fallback to silent cache update if unsupported (next hover gets enriched data) (AC: 4)

  11. Add cache-busting on file save events in backend.rs — Implement textDocument/didSave handler, compute file hash using blake3 crate, send DELETE /api/v1/cache/hover/{file_hash} to A2A, and clear cache with versioned prefixes or a single file-scoped key instead of SCAN + DEL (AC: 6)

  12. Add health check polling to lsp-deps in justfile — Create lsp-deps target running skeleton-up, poll health endpoints (Oxigraph :7878/health, Redis :6379 PING, A2A :8001/health, OPA :8181/health) with 3 retries every 2s from doctor.sh pattern, ASCII output with response times, and explicit timeouts/auth where needed (AC: 7)