Voice Lifecycle and Deployment Readiness Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.
Goal: Eliminate the observed SIP teardown close race and make measured room-join latency, environment-scoped alerts, and regional operations part of the voice deployment contract.
Architecture: SIP sessions disable SDK disconnect auto-close and route the caller-disconnect event directly to a single context-shutdown request. The deployment workflow keeps control-plane status as a coarse gate while a production smoke verifier enforces the 5000 ms dispatch-to-join SLO and emits explicit deployment identity for cross-replica ClickHouse queries.
Tech Stack: TypeScript, Vitest, LiveKit Agents 1.5.1, Bash, GitHub Actions, OpenTelemetry, ClickHouse/HyperDX.
Spec: docs/superpowers/specs/2026-08-25-voice-lifecycle-readiness-design.md
Global Constraints
- Keep
@livekit/agentsat 1.5.1; do not add a dependency patch. - Keep
LIVEKIT_NUM_IDLE_PROCESSES=2,jobMemoryWarnMB=2000, andjobMemoryLimitMB=2400. - Enforce a 5000 ms dispatch-to-agent-join SLO.
- Keep the voice worker network-isolated from backend, database, and Redis code.
- Use strict TypeScript with no
anyand no type assertions exceptas const. - Run behavior tests on the PR branch, not locally; run local type, lint, shell, actionlint, and diff checks.
- Keep every ClickHouse query bounded by
ServiceNameandTimestampTime.
Task 1: Make SIP disconnect shutdown single-owner
Files:- Create:
backend/voice-agent/src/voice-room-input-options.utils.ts - Create:
backend/voice-agent/src/voice-room-input-options.utils.spec.ts - Modify:
backend/voice-agent/src/index.ts
-
Produces:
VoiceRoomInputOptionsUtils.forCall({ isSipCall }): { noiseCancellation: NoiseCancellationOptions; closeOnDisconnect?: false }using the existing telephony/WebRTC noise-cancellation constructors. -
Consumes: existing
ShutdownCrashGuard,beginCallTeardown,ctxShutdownInitiated, andctx.shutdown(reason)lifecycle state inindex.ts. - Step 1: Write the failing room-input test
closeOnDisconnect: false while WebRTC options omit the override and preserve their respective noise-cancellation objects. The production mutation caught is re-enabling SDK SIP auto-close and recreating the double-close race.
- Step 2: Push the test-only commit and verify CI fails for the missing utility
VoiceRoomInputOptionsUtils or the SIP result lacks closeOnDisconnect: false.
- Step 3: Implement the minimal input-options utility and wire it into
session.start
- Step 4: Replace SIP
session.shutdownwith guarded direct context shutdown
beginCallTeardown(), arm shutdownCrashGuard, set ctxShutdownInitiated=true, then call ctx.shutdown('participant_disconnected'). Keep the Close listener idempotent.
- Step 5: Verify the PR test is green before continuing
Task 2: Preserve structured teardown errors
Files:- Create:
backend/voice-agent/src/shared/telemetry.spec.ts - Modify:
backend/voice-agent/src/shared/telemetry.ts
-
Consumes:
logArgs(...args: unknown[]). -
Produces: nested error attributes
<key>.name,<key>.message, and optional<key>.stack, while preserving top-levelerror.messageanderror.stack. - Step 1: Write failing serialization tests
logArgs('teardown failed', { _e: new Error('activity disappeared') }) against literal _e.name and _e.message values, plus a circular non-error object to preserve current best-effort behavior. The production mutation caught is silently dropping the nested _e object.
- Step 2: Verify CI reports the missing nested attributes
_e.message is undefined.
- Step 3: Implement minimal nested Error flattening
Error before JSON serialization, add the three deterministic fields, and leave all other values unchanged.
- Step 4: Verify the PR telemetry spec is green
Task 3: Enforce measured room-join readiness
Files:- Create:
backend/scripts/voice-smoke/join-latency.utils.ts - Create:
backend/scripts/voice-smoke/join-latency.utils.spec.ts - Modify:
backend/scripts/voice-smoke-test-verify.ts - Modify:
.github/workflows/voice-agent-deploy.yml
-
Produces:
VoiceSmokeJoinLatencyUtils.measure({ dispatchStartedAtMs, agentJoinedAtMs, maximumMs }): { latencyMs: number; withinSlo: boolean }andDEFAULT_AGENT_JOIN_SLO_MS=5000. -
Consumes: existing
setAttemptAttrandfailSmokeAttemptin the verifier. - Step 1: Write failing boundary tests
- Step 2: Verify CI fails because the latency utility is absent
- Step 3: Implement the utility and instrument the verifier
createDispatch, measure when the remote agent appears, emit smoke_test.agent_join_latency_ms and smoke_test.agent_join_slo_ms, log the duration, and fail slow attempts with agent_join_slo_exceeded.
- Step 4: Pin the 5000 ms workflow value for CI and production smoke invocations
VOICE_SMOKE_MAX_AGENT_JOIN_LATENCY_MS: 5000 in both verifier steps so the reviewed workflow makes the SLO explicit.
- Step 5: Verify the PR latency tests and deployment guards are green
Task 4: Correct worker and rollout readiness configuration
Files:- Modify:
backend/voice-agent/src/agent.config.spec.ts - Modify:
backend/voice-agent/src/agent.config.ts - Modify:
.github/scripts/wait-for-livekit-agent-readiness.sh - Modify:
backend/voice-agent/src/__tests__/deploy-workflow.guard.spec.ts - Modify:
.github/workflows/voice-agent-deploy.yml
-
Produces:
WORKER_SERVER_OPTIONS.initializeProcessTimeout=90_000. -
Produces: readiness script behavior that requires two consecutive Running samples, logs replica counts, and times out after 15 minutes without requiring
current>=1. - Step 1: Change tests first
lk command whose status is Running with 0 / 1 / 16; assert success after two polls. Add a timeout/failure case for never-Running status.
- Step 2: Verify CI fails on the old 60-second and replica-count gates
0 / 1 / 16 never becomes ready.
- Step 3: Implement the timeout and readiness changes
15m, readiness deadline/error text to 15 minutes, and make Running the evaluated state while counts remain diagnostics.
- Step 4: Verify PR configuration and script tests are green
TIMEOUT: 15m.
Task 5: Scope deployment telemetry and alert guidance
Files:- Modify:
.github/workflows/voice-agent-deploy.yml - Modify:
backend/voice-agent/src/__tests__/deploy-workflow.guard.spec.ts - Modify:
backend/voice-agent/docs/DEBUGGING.md - Modify:
backend/voice-agent/load-test/FINDINGS.md
-
Produces deployment resource attributes
deployment.environment,deployment.region, andlivekit.agent.idfor CI and production agents. - Produces checked-in ClickHouse queries for production memory kills, sustained warnings, supervisor failures, join latency, and cross-replica call diagnosis.
- Step 1: Add failing deployment-identity guards
- Step 2: Verify CI fails on missing deployment identity
OTEL_RESOURCE_ATTRIBUTES update exists.
- Step 3: Pin explicit attributes through existing update-secrets steps
- Step 4: Add the operational runbook and rollout findings
lk agent logs, bounded ClickHouse queries, the corrected alert split, and the 5000 ms join SLO. Record the 2026-08-25 production evidence and the decision not to loosen memory limits or raise the warm pool.
- Step 5: Run non-test verification
pnpm tsgo, lint each edited TypeScript file, bash -n on edited shell scripts, actionlint on the workflow, and git diff --check. Do not run behavior tests locally.
- Step 6: Push implementation and monitor required PR checks
Task 6: Post-deploy evidence after merge
Files:- No repository changes unless monitoring exposes a regression.
- Consumes: production smoke span attributes and explicit deployment resource attributes.
- Produces: observed join latency, MoneyGram inbound-call health, teardown status, and regional deployment state for the handoff.
- Step 1: Do not merge this follow-up PR without explicit user instruction
- Step 2: After a future approved merge/deploy, query the new-version window