Skip to main content

UCSD Process Discovery is a voice-interview platform for finding repetitive administrative work, extracting evidence-backed process records, and turning those records into a ranked automation backlog for forward deployed engineering.

This page describes the production architecture, privacy boundaries, data flow, model routing, and safeguards used by the application. It reflects the current runtime configuration without exposing API keys, secrets, participant records, or admin console data.

System at a glance

UCSD Process Discovery architectureParticipants join a Vapi voice call from their browser. Vapi reports the call to the Next.js app on Vercel through a webhook. The app stores everything in Neon Postgres, which also queues analysis. The analysis pipeline runs on Vercel functions and calls on-premises models through the UC San Diego Triton AI gateway. Raw transcripts are redacted on campus first; later stages only see redacted text. Results feed the admin console views.PEOPLE AND VENDOR CLOUDSAPPLICATION ON VERCEL AND NEONANALYSIS PIPELINE (VERCEL FUNCTIONS, POSTGRES AS THE QUEUE)UC SAN DIEGO CAMPUSAdmin console usersOwners, admins, FDE, viewersMagic-link sign-in, no passwordsTemplates, campaigns, invitationsVapi voice cloudWebRTC audio transport (Daily.co)Deepgram transcriptionAnthropic conversation modelResendInvitations, reminders, sign-in linksDelivery events reported backBounces only affect invite mailInterview participantOpens a personal link, consentsPasses the mic checkTalks to the agent in the browserNext.js app on VercelAdmin console and interview pages; every server action validates its input/api/vapi/webhook: shared-secret check, idempotent, session bound by call idCron sweeps: calls, analysis, reminders, retention, nightly reclusterNeon Postgres with pgvectorSource of truth for every tableAnalysis queue: stage cursor, lease,attempts, cost ledger, model flagsVercel BlobAudio recordings, campaign optionDeleted by the retention jobTranscripts stay in PostgresSix stages, resumable from the last completed oneOnly the redact stage sees raw text. Every later stage works on redacted text.redactextractscoreembedclusterrollupTriton AI gatewayOpenAI-compatible endpoint on campus; separate on-prem and cloud keys; routes each call to a modelOn-prem modelsGemma 4 and DeepSeek V4 for redactionDeepSeek V4 for extraction, themes,adjudication and dossierstgpt embeddings (1024 dimensions)What stays on campusRaw transcript redaction never uses acloud model unless an operator opts in.Any fallback or off-campus call isflagged on the run and shown to admins.Outputs in the admin consoleRanked backlog, heatmap, systems, synthesized themes, dossiers, reports, and Ask the interviewssends invitesmagic linklive audio, never through Vercelstatus, tool calls, end-of-call reportconsent, session, state (by token)consolereads and writesaudio copyclaims queued runs, writes records, clusters, themesmodel calls (redacted text after stage one)inferenceserved from Postgres

Live audio and raw transcripts pass through vendor clouds (Vapi, Vercel, Neon). Analysis models run on campus through the Triton AI gateway; only the redaction stage sees raw text, and everything after it works on redacted text. Download this diagram (SVG).

Architecture

The web application is a Next.js 16 App Router deployment on Vercel, rendered with React 19. The user interface is Bootstrap 3 served through the UC San Diego Decorator v5 bundle, self-hosted with the app. The admin console manages templates, campaigns, invitations, consent documents, interviews, analysis results, and exports. The interview participant flow is token based: the participant opens a personal invitation link, accepts the consent notice, passes the microphone check, and starts the voice interview.

The realtime audio call does not run through Vercel. The participant browser connects directly to Vapi over WebRTC (Daily.co transport) using the Vapi browser SDK. The app assembles a transient Vapi assistant immediately before each call. That assistant includes the current interview template, consent script, participant context, resume state, configured voice, model choice, per-call duration cap, and tool definitions, but it does not include server secrets. Because the assistant configuration transits the participant's browser, the server keeps a hash of the prompt, tools, duration cap, recording settings, and metadata and compares it with what Vapi actually ran at the end of the call. A mismatch parks the interview for human review and it is never analyzed.

Vapi sends status updates, tool calls, transcripts, summaries, and end-of-call reports back to /api/vapi/webhook. The webhook endpoint verifies the org-level Vapi secret, stores every webhook behind an idempotency gate, resolves the call session by the Vapi call id bound by the token-authenticated browser, ignores tool calls and reports from superseded or never-connected sessions, updates the call session, and kicks off the finalization path. Cron sweeps reconcile missed or delayed events, so webhooks are the fast path and polling is the durability backstop.

Neon Postgres (with pgvector) is the source of truth, accessed through Drizzle. It stores campaigns, invitations, interview state, call sessions, stitched transcripts, redacted transcripts, analysis runs, process records, embeddings, clusters, themes, consent events, email events, and an LLM usage ledger. Postgres also acts as the queue for analysis work: each run has a stage cursor, attempt count, lease, cost ledger, model flags, and status. Admin sign-in uses Auth.js v5 email magic links; email goes through Resend; audio recordings are copied to Vercel Blob; scheduled jobs run on Vercel Cron.

Participant flow

  1. Invitation: Resend sends a magic link from the verified sender. Links are personal, expire by campaign policy, and rotate whenever they are resent or copied.
  2. Written consent: the participant accepts a versioned written consent notice before any call starts. The click is recorded as a consent event pinned to the document version.
  3. Voice session: the app mints a call session and transient Vapi assistant. The participant speaks with the voice agent in the browser. Only one call may be live per interview; starting again from another device takes over the interview and ends the superseded call.
  4. Spoken consent: the agent reads the consent script and calls the confirm_consent tool with the participant's answer. The participant's own words are stored as evidence. Section completion is refused until a verbal yes is on record, so the agent cannot skip past consent. A verbal decline after content has been captured withdraws the interview and records a withdrawal event.
  5. Tool calls: the agent marks sections complete and can pause at the participant's request. Duplicate tool-call deliveries replay the stored result instead of acting twice.
  6. Pause and resume: a pause requested by voice or by the Pause button stays a pause, as does a dropped connection or a call that hits the duration cap. Every pause extends the link's resume window by seven days. The next call receives a progress summary and resumes at the next unfinished section.
  7. Completion: when the agent concludes the call with spoken consent on record, the transcript is stitched across sessions, the invitation is marked complete, a receipt can be sent, and analysis is queued. If the agent concludes a call without spoken consent on record, the interview is paused for re-confirmation rather than completed, and the same check blocks finalization and analysis.

Voice stack

Voice stack components
LayerCurrent implementationPurpose
Realtime call transportVapi browser SDK over WebRTC (Daily.co)Keeps low-latency audio off the Vercel request path. Campaigns can set the hard call cap up to 43,200 seconds.
Conversation modelVapi-native Anthropic, default shown to signed-in adminsRuns the live voice interview and adapts the template questions into natural conversation.
Optional conversation overrideA campaign can point the live conversation at an OpenAI-compatible endpointAllows an OpenAI-compatible endpoint, such as the Triton gateway, to power the live interview after latency testing. The endpoint key lives in Vapi's credential store, never in the assistant config.
TranscriptionDeepgram nova-3 through VapiProduces the call transcript; template key terms are passed through to improve campus jargon recognition.
VoiceCampaign voice provider/voice id, default Vapi ElliotSpeaks the interviewer side of the conversation.
Operational Vapi analysisMinimal summary and coverage JSON inside VapiProvides lightweight call-quality signals only. Authoritative extraction happens server-side after redaction.

Analysis pipeline

The analysis pipeline is deliberately staged so each expensive or risky task has a narrow job. A run can resume from its last completed stage and can be re-run without changing prior successful runs until the new run succeeds. Pipeline version: 1.0.

Analysis stages
StageWhat it doesOutput
RedactApplies deterministic regex rules (identifiers, contact details, and titled personal names such as Prof. or Dr. followed by a name), then asks two on-campus models in parallel to list the remaining names and personal identifiers and unions their answers. An empty answer on a transcript that looks like it contains identifiers triggers a second opinion from another on-campus model. This is the only stage that sees raw transcript text, and it fails closed: if no on-campus redaction model answers, nothing downstream runs.Versioned redacted transcript.
ExtractInventories recurring work processes, extracts one structured process record per process, and captures non-process theme mentions. Evidence quotes are required for factual process claims. A deterministic grounding validator then checks every quote against the redacted transcript: fuzzy matches are accepted only when every number or number word in the quote appears exactly. A numeric field is kept when a verified quote is labelled for it (tolerating misspelled labels) or plainly states the number, including phrases such as one in five, once a month, or six hours; otherwise it is nulled and added to the record's unknowns. The pain quote is dropped when it cannot be found, and a record with fewer than six verified quotes and no verified volume quote is dropped rather than entering the backlog.Process records, theme mentions, verified evidence, unknowns.
ScoreComputes effort, pain, feasibility, confidence, and routing signals from extracted facets. Pure math, no model call.Priority fields used by the backlog.
EmbedEmbeds redacted transcript chunks, process records, and theme mentions.pgvector-searchable corpus for clustering and Ask.
ClusterJoins similar process records across interviews, with LLM adjudication in the ambiguous range. Human curation (must-link, cannot-link, rename, merge, split) is keyed on a stable identity made from the interview, the normalized process name, and the department, so it survives re-analysis with new prompts or models. Renames apply on every recluster and carry through merges.Canonical process clusters and membership.
RollupAggregates clusters, department heatmaps, systems, and campaign-level theme synthesis. Only succeeded runs count, and an interview's older runs are excluded as soon as a new run starts. Theme synthesis is incremental (only unassigned mentions are sent), chunked so the model's output cap cannot truncate a grouping, wrapped in one transaction, and serialized with a per-campaign advisory lock so the nightly job, admin recompute, and a finishing run never interleave.Backlog, heatmap, systems, themes, and analytics views.

Retries distinguish real errors from interruptions. An error attempt is counted against the run; a lease reclaim (a crash, a deploy, or the sweep's time budget expiring mid-stage) re-queues the run from its stage cursor without consuming an attempt, and is capped separately. Every model call has its own wall-clock timeout, and a run that trips the cost ceiling is failed permanently rather than retried.

Each run has a hard cost circuit breaker of shown to signed-in admins. On-prem Triton stages are recorded as zero marginal LLM cost; direct/cloud model usage is estimated from token counts.

Model routing

Analysis calls use model specs in the form onprem:<id>, cloud:<id>, or anthropic:<id>. Bare model IDs are treated as direct Anthropic models for backward compatibility. The current environment reports:

Current model routing
PurposeCurrent primary modelFallbacksWhy this role uses it
Live voice conversationshown to signed-in admins via Vapi-native Anthropic, unless a campaign overrides itCampaign-specific overrideOptimized for low-latency turn-taking and natural interviewing rather than bulk analysis.
Redactionshown to signed-in adminsshown to signed-in adminsFast entity listing after regex redaction; this is the only analysis model stage that sees raw transcript text, so its chain is restricted to on-campus models.
Process extractionshown to signed-in adminsshown to signed-in adminsProduces strict structured output for recurring processes, evidence, unknowns, systems, time, volume, and route signals. Chosen because it is the on-prem model that returns the full process-record schema reliably.
Cluster adjudicationshown to signed-in adminsshown to signed-in adminsResolves ambiguous semantic matches between process records when vector similarity alone is not decisive.
Themesshown to signed-in adminsshown to signed-in adminsConsolidates raw theme mentions into campaign-level findings with descriptions, source breadth, requirement implications, recommended follow-up, and cited evidence. Falls back to embedding-similarity grouping if every model in the chain fails.
Dossiersshown to signed-in adminsshown to signed-in adminsProduces deeper opportunity write-ups for FDE review after a backlog item is selected. Generated on demand from the backlog.
Ask the interviewsshown to signed-in adminsNone by default for chat synthesis.Uses retrieval over analyzed records and redacted transcript chunks, then returns cited answers grounded in the campaign corpus.
Embeddingsshown to signed-in admins (1024 dimensions)Configurable after vector schema validation.Powers semantic search, clustering, Ask retrieval, and theme/process similarity. Only redacted text is embedded.
QA harnessshown to signed-in adminsDevelopment-onlyDrives synthetic test interviews where roleplay quality matters more than production cost. Synthetic transcripts contain no real data.

Fallback policy. When a structured stage's primary model fails or returns invalid output, the run tries the on-campus fallbacks first and a cloud model only when a cloud key is configured. Redaction never falls back to a cloud model unless the operator explicitly opts in; without an on-campus answer it fails closed. Every fallback or off-campus call is recorded on the analysis run and shown on the admin interview page, and a complete usage ledger records model, tokens, cost, and on-campus status for every pipeline stage, dossier, Ask chat turn, and nightly adjudication call.

Prompt versions: redact v2, extract v2, cluster v1, themes v2, dossier v1.

Privacy and data boundaries

  • Raw transcript text is stored so administrators can audit interviews, but the analysis pipeline redacts before downstream extraction, embeddings, clustering, and Ask retrieval.
  • Deterministic regex rules remove common identifiers such as SSNs, UCSD PID-shaped IDs, emails, phone numbers, dates, and titled personal names before the redaction models run.
  • Two on-campus redaction models are each instructed to remove third-party names and personal identifiers while preserving systems, departments, job roles, buildings, and process vocabulary; their answers are unioned. Redaction runs only on campus infrastructure unless the operator explicitly opts into a cloud model.
  • Campaigns can anonymize reports so FDE-facing views refer to people by role and department rather than name or email. Viewers never see or export participant names or emails on any campaign.
  • Theme synthesis and Ask use active successful analysis outputs; failed and superseded analysis runs are not counted in current campaign themes.
  • Audio recording is campaign-controlled. If a retention period is configured, the daily retention job deletes expired recordings, strips raw transcript and recording fields from stored webhook payloads, and removes the raw per-call transcript from analyzed call sessions. The redacted transcript in the analysis tables is the durable copy; rows are never deleted.
  • Consent is versioned and captured twice: click-through consent before the call and spoken confirmation during the call. Both are enforced server-side, and the participant's spoken words are stored as evidence.

Operations and reliability

The app uses Vercel Cron and Postgres leases instead of a separate queue service. The call sweep reconciles Vapi calls whose end-of-call report never arrived, retries recording fetches that failed, and closes sessions that were minted but never bound to a call. The analysis sweep resumes queued or lease-expired analysis runs, reminders send pending follow-ups, the retention job expires overdue invitations, auto-finalizes paused interviews after 48 hours of inactivity, and enforces recording and transcript retention, and the nightly recluster job keeps campaign rollups current.

All important external inputs are made idempotent. Vapi webhooks are stored before processing and duplicate tool calls replay their stored result, email sends are logged as email events, analysis stages are wipe-and-rewrite or versioned where appropriate, and successful re-analysis supersedes prior successful runs only after the new run completes.

Access and security

  • Admin sign-in uses emailed magic links with an allowlist; there are no passwords. A disabled admin loses access on their next request because the session is re-checked against the user record, and the last active owner cannot be demoted or disabled.
  • Roles are owner, admin, fde, and viewer. Viewers see aggregate views and exports only; person-level and dossier exports are refused for viewers on non-anonymized campaigns, and the Service Owner report is name-masked for them.
  • Every admin server action validates its input with zod (ids, bounded integers, model-id allowlist, real email addresses, bounded invite metadata). Magic-link requests, invitation sends, reminders, invite-link minting, and report emails are rate limited.
  • CSV exports neutralize spreadsheet formula prefixes. Exports and API responses are marked non-cacheable.
  • In production the server validates its required configuration on the first request that needs it and fails with a clear message rather than falling back to a localhost default.
  • Every page carries Strict-Transport-Security, nosniff, frame denial, a strict referrer policy, a Permissions-Policy that allows the microphone only from this origin, and an enforced Content Security Policy with a per-request script nonce. Invitation and sign-in tokens are stored only as SHA-256 hashes. Vapi webhooks are checked against the org-level shared secret with a constant-time comparison; Resend webhooks are signature-verified.

Verification

Continuous integration runs the typecheck, lint, the unit-test suite, a production build, a dependency audit, a prompt-version consistency check, and a schema-drift check that fails if the database schema changed without a committed migration.

Two live-lifecycle regressions drive the real interview routes and webhook handler against a running dev server with synthetic Vapi events, then let the analysis pipeline run on the on-prem models. The happy path covers consent, sections, end of call, and analysis. The integrity-gates run covers section completion refused before spoken consent, a voice pause staying a pause, duplicate tool-call replay, device takeover neutralizing the old call, an agent-concluded call without consent being paused rather than completed, a verbal decline abandoning the interview, and the analysis run recording no off-campus use. A golden-fixture suite runs the whole pipeline on four synthetic personas with planted identifiers and known ground truth.

None of these replace a real voice call. Audio, turn-taking, and the agent actually calling the tools are only proven live.

Release Notes

2026-09-02: Hardening pass: consent, interview integrity, model policy, access

  • Spoken consent is now enforced server-side: the agent cannot mark a section complete until confirm_consent records a verbal yes, the participant's words are stored as evidence, and finalization and analysis are blocked without it. A verbal decline after content withdraws the interview; an agent-concluded call with no spoken consent is paused for re-confirmation, never completed.
  • A voice-initiated pause stays a pause with a seven-day resume extension. Taking over an interview from a second device ends the superseded call, and the webhook ignores tool calls and reports from superseded sessions.
  • The assistant config hash now covers the prompt, tools, duration cap, recording settings, and metadata; a mismatch parks the interview for human review instead of analyzing it. Recording fetch failures and never-bound sessions are reconciled by the call sweep.
  • Model routing re-verified against the Triton gateway. On-prem fallbacks run before any cloud model, redaction never leaves campus unless explicitly enabled, and every fallback or off-campus call is recorded on the run and shown on the interview page. A complete usage ledger now covers pipeline stages, dossiers, Ask chat, and nightly adjudication.
  • Curation overrides key on a stable process identity and survive re-analysis; renames carry through merges. Grounding requires exact numeric tokens, nulls numeric fields with no verified evidence, and verifies the pain quote. Theme synthesis is transactional, incremental, chunked, and serialized per campaign; rollups count only succeeded runs.
  • Access hardening: disabled admins lose their session on the next request, the last active owner cannot be demoted or disabled, viewers never see or export participant names or emails, every server action validates its input, outbound email is rate limited, CSV exports neutralize formula prefixes, production configuration is validated at first request, and security headers plus an enforced Content Security Policy with per-request nonces are set on every page.
  • Retention now strips raw transcript text from stored webhook payloads and from analyzed call sessions once the campaign retention period passes; the redacted transcript in the analysis tables is the durable copy.
  • Two live-lifecycle regressions and a golden-fixture pipeline suite were added alongside the CI checks. The golden suite drove further deterministic hardening: two on-campus models are unioned for redaction with a titled-name regex rule, grounding recovers numbers stated in verified quotes (misspelled labels, one in five, once a month, six hours), and unsubstantiated process records are dropped.
  • Content Security Policy is enforced with per-request nonces from a proxy, the UC San Diego Decorator bundle is deferred on interviewee pages, the template builder and email templates were split into modules, and the sign-in page no longer reveals allowlist membership.

2026-06-30: Public About page, release notes, and richer theme synthesis

  • Made this About page publicly accessible at /admin/about so stakeholders can review the platform architecture without console access.
  • Made the How to use guide publicly accessible at /admin/guide with the same public navigation and sign-in affordance.
  • Added the public About and How to use sections to signed-out and signed-in application navigation.
  • Added release notes and refreshed the architecture, analysis, privacy, operations, and model-routing documentation.
  • Upgraded Themes from raw one-off observations into campaign-level synthesized themes with source breadth, evidence links, requirement implications, and recommended follow-up actions.
  • Theme consolidation now ignores failed or superseded analysis runs so re-analysis does not double-count stale theme mentions.

2026-06-29: Higher runtime caps and resilient analysis budgets

  • Raised the Vapi per-call hard cap to 43,200 seconds, matching Vapi's documented 12-hour maximum, while keeping the default target interview length concise.
  • Made the per-run analysis cost ceiling configurable with a $5 default instead of a hardcoded $0.75 breaker.
  • Re-ran the Atlassian EOL interview that had failed on the old analysis ceiling; it now completes analysis successfully.

2026-06-24: Campaign cleanup and stale-rollup protection

  • Added campaign-admin participant removal with explicit confirmation and cleanup of related interview, call-session, transcript, recording, and analysis data.
  • Refreshing campaign clusters, themes, and dossier freshness after deletion prevents aggregate views from retaining removed participant data.

2026-06-11: Email delivery observability

  • Added Resend webhook ingestion and email event tracking so invitation status can distinguish sent mail from delivered, bounced, complained, or failed mail.
  • Kept the existing verified sending domain while improving delivery-state visibility.