feat: run OpenAI Realtime drift probes live in CI via OPENAI_API_KEY fallback - #324
Conversation
commit: |
CI live result — the probes now RUN, and they surface a real problem
What this meansThe realtime socket connected (the client received one message), but that Auth-scope vs stale model id — leaning stale modelThe connect-then-error-event pattern argues against a hard auth rejection (a Secondary findingBecause the probe throws a raw connection timeout rather than producing a Recommendation (out of this PR's scope)Hold this draft. The un-skip is correct and working; landing it needs a |
…seline) (#330) ## What Gates OFF the fal LIVE queue-lifecycle drift leg so it no longer runs in CI / the shared base drift collector, while KEEPING all of fal's static drift coverage. The live probe (`falQueueLifecycleCanary`, driven by the live block in `src/__tests__/drift/fal-queue.drift.ts`, merged in #327) fails against real fal: `flux/schnell` returns `status:"IN_PROGRESS"` immediately (not the assumed `IN_QUEUE`) → AssertionError → collector exit-5 quarantine → `drift-live-pr` **base** step (which runs main's code) reds on EVERY PR, blocking #324 and #325 from merging. A proper fix for the probe is being built in parallel (`fix/fal-probe-in-progress`). This PR is the fast, reversible descope so coverage PRs can land now. ## The change (one hunk) Added an explicit opt-in env gate to the live `describe` — it now skips unless BOTH are set: ```ts describe.skipIf(!FAL_KEY || !process.env.FAL_LIVE_QUEUE)("fal.ai queue lifecycle (live, cost-safe)", ...) ``` `FAL_LIVE_QUEUE` is not set in CI, so the live leg is skipped everywhere. Static fal tests (`fal.drift.ts` + the mock-vs-exemplar tests in `fal-queue.drift.ts`) are untouched. Re-enable by removing the `FAL_LIVE_QUEUE` gate once the probe is fixed. ## Red / green (local) **RED** (unmodified main, `FAL_KEY` set) — live leg runs and fails: ``` × fal.ai queue lifecycle (live, cost-safe) > real submit + status + cancel ... → INFRA_ERROR: fal queue submit: API returned 401 ... Tests 1 failed | 4 passed (5) ``` **GREEN** (this PR, `FAL_KEY` set, no `FAL_LIVE_QUEUE`) — live leg skips, no AssertionError: ``` ✓ src/__tests__/drift/fal-queue.drift.ts (5 tests | 1 skipped) 22ms Tests 4 passed | 1 skipped (5) ``` **Re-enable path verified** (`FAL_KEY` + `FAL_LIVE_QUEUE` both set) — live leg runs again. Full `pnpm run test:drift` (CI-style, no `FAL_KEY`): **93 passed | 65 skipped**. Typecheck, prettier, eslint, build all clean. ## Expected CI note (fix-forward) This PR's own `drift-live-pr` will be RED at the **base** step because base runs main's still-broken fal probe — that red is exactly the poison this PR removes, and it cannot go green pre-merge (base runs old main). All other checks are expected green. Merging fix-forward heals main so subsequent PRs' base steps no longer run the fal live leg. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Gate the 3 realtime WS protocol probes on OPENAI_REALTIME_KEY ?? OPENAI_API_KEY so they run live in CI via the existing key instead of skipping. No new secret.
…retired -preview id)
OpenAI retired the Realtime Beta API — a live Beta handshake now returns
{"code":"beta_api_shape_disabled","message":"The Realtime Beta API is no
longer supported. Please use /v1/realtime for the GA API."}. The drift leg's
Beta branch therefore hit a dead endpoint, surfaced an unparseable error, and
quarantined the whole leg (exit 5), turning drift-live-pr red at HEAD.
Remove the Beta branch from openaiRealtimeWS (GA-only: session.type:"realtime"
+ output_modalities) and delete the now-dead GA<->Beta consistency probe plus
its normalization constants. GA is the only live surface and the only surface
aimock mocks.
04c8890 to
ab8db68
Compare
What
Un-skips the 3 OpenAI Realtime WS protocol/event-shape probes in
src/__tests__/drift/ws-realtime.drift.tsso they run live in daily CI:WS text event sequence and shapes match (GA)WS tool call event sequence matches (GA)GA and Beta event sequences are consistent after normalizationPreviously these gated on
it.skipIf(!process.env.OPENAI_REALTIME_KEY). NoOPENAI_REALTIME_KEYsecret exists in CI, so they always skipped — onlythe model-listing canary (which reads
OPENAI_API_KEY) actually ran. DRIFT.mdcalled Realtime "Verified", but that was only ever true with a manually
supplied local key.
The change (reuse a key, don't add one)
OpenAI Realtime authenticates with the standard project OpenAI key — scope
is project-level; there is no separate "realtime key" type. So the correct fix
is to resolve the credential as:
OPENAI_REALTIME_KEYas an optional preferred override (for therare case where realtime access lives on a different project).
OPENAI_API_KEY, which is already a repo secretand already threaded into the drift job env in both
test-drift.ymlandfix-drift.yml(used by every other OpenAI leg). No workflow change needed.Net effect: this removes a secret dependency rather than adding one, and
flips the 3 probes from SKIP → RUN in CI.
Cost-minimal scoping
The probes were already the cheapest possible realtime interaction and this PR
keeps them that way: text modality only (
modalities: ["text"]), the cheapestrealtime model (
gpt-4o-mini-realtime-preview), one-word prompts ("Say hello","Weather in Paris"), no audio streaming, session closed at
response.done.It's a cheap daily protocol canary, not a full realtime exercise.
Red-green (structural, local)
The live probes need a real OpenAI key (unavailable in this dev env), so the
live validation is deferred to CI (see risk note below). The structural
red-green exercises the real failure surface — the drift triangulation +
Surface:-marked report used by the realtime probes(
compareSSESequences+formatDriftReport(..., "openai-realtime")),network-free:
RED — drop a real event type from the mock leg:
GREEN — shapes aligned:
Gate-flip verified network-free: with
OPENAI_API_KEYset andOPENAI_REALTIME_KEYunset, oldskipIf(!OPENAI_REALTIME_KEY)⇒ skips;new
skipIf(!OPENAI_REALTIME_CREDENTIAL)⇒ runs; explicit override stillwins when present.
What CI confirms or refutes (the one risk)
This PR's premise is that this project's
OPENAI_API_KEYhas Realtime modelaccess. CI is the live validation. If the now-running realtime probes fail
with an auth/scope error, it means this project's key lacks Realtime access
— in which case the fallback is wrong and we genuinely need a separate
OPENAI_REALTIME_KEY(acquisition covered in the runbook). Any otherdiff — event names/shapes — is real protocol drift the canary is meant to catch.
Notes
(
src/__tests__is excluded from every tsconfig; ~113 pre-existing errors inother test files are untouched).
🤖 Generated with Claude Code