← myTrajectory

myTrajectoryTechnical Reference

This page covers the technical detail behind myTrajectory as implemented in the current codebase: architecture layers, the full tech stack, data model, Cloud Functions, security patterns, the design system, and testing approach. The platform is currently in live testing with two charity partners, with the recruiter-facing layer in active discussion with recruitment professionals. For the product thinking and design rationale, start with the narrative.

System Overview

The application is built as a layered architecture with clear separation between client, server, AI, and data concerns.

1Client LayerNext.js App Router, React, Tailwind, Radix UI
2API Route LayerAuth guards, validation, rate limiting, idempotency
3Service LayerBusiness logic, data access, transformation
4AI GatewayMulti-provider orchestration, intent routing, cost tracking
5Data LayerFirestore, Cloud Storage, real-time subscriptions
6Cloud FunctionsFirestore triggers, scheduled jobs, async processing
7MonitoringSentry, structured logging, request tracing

Tech Stack

Frontend & framework

TechnologyPurpose
Next.js 16 (App Router)Full-stack React framework with server components and API routes
React 19Component model with hooks and context
TypeScript 5 (strict)Type safety across client and server
Tailwind CSS 3.4Utility-first styling with custom design tokens
Radix UIUnstyled accessible primitives (20+ components)
shadcn/ui56 reusable UI component files in src/components/ui, built on Radix patterns
React Query v5Server state management with caching and background refetch
Framer MotionAnimation library for transitions and micro-interactions
RechartsData visualisation (sparklines, activity charts)
React Hook Form + ZodForm management with schema validation

Backend & infrastructure

TechnologyPurpose
Firebase AuthAuthentication with custom claims and role-based access
FirestoreReal-time NoSQL database with security rules
Cloud FunctionsFirestore triggers and scheduled jobs for async processing
Cloud StorageCV and user file storage
Cloud BuildCI/CD pipeline with typecheck, lint, and test gates
ResendTransactional email delivery with RFC 8058 unsubscribe

AI providers

ProviderRole
Google GeminiDefault routed provider, fast and thinking tiers
OpenAISupported via routing
Anthropic ClaudeSupported via routing

Quality & monitoring

TechnologyPurpose
VitestUnit and integration testing (NOT Jest)
PlaywrightCritical-path end-to-end browser testing
SentryError tracking across client, server, and edge
ESLint + HuskyLinting and pre-commit hooks

Feature Landscape

myTrajectory spans several feature domains, with shared data and service layers connecting them.

4.1 Core workspace

CV Management

Upload PDFs or paste text. Cloud Functions automatically extract content, run multi-pass AI analysis, and populate the Experience Record. Supports version control with delta analysis, CV tailoring for specific roles, and CV-to-JD coverage matching.

  • Automatic text extraction via Cloud Functions
  • 3-pass AI feedback system (summary, strengths, improvements)
  • Experience Record initialisation from extracted content
  • CV ranking for role fit assessment

Job Application Tracking

Full lifecycle management from discovery to offer. Kanban-style status progression with rich metadata for each application.

  • Status pipeline: Saved → Assess → Apply → Submitted → Interviewing → Offer → Closed
  • Application materials: cover letters, custom answers, take-home tasks
  • Interview round tracking with type, date, location, interviewer details
  • Communication log: phone, email, meeting, LinkedIn messages
  • AI-powered JD extraction (8–20 structured requirements with importance tiers)
  • CV-to-JD coverage analysis: strong, partial, or gap per requirement

STAR Answer Library

Structured interview story capture with AI-powered review at both the section and whole-answer level. Two distinct input modes serve different user preferences.

  • Per-section coaching: situation, task, action, result reviewed individually
  • Full answer grading: clarity, conciseness, impact, relevance
  • Star ratings (1–5): specificity, impact, relevance
  • Auto-suggested competency tags and follow-up questions
  • Progressive-unlock accordion UI for step-by-step review
  • Experience node linkage, tying stories to specific CV roles
  • Custom competency categories with per-category answer counts
  • Print-optimised export view with configurable font size and page breaks

Story Companion (Voice Agent)

The Story Companion is a conversational AI workflow for structured STAR story capture. Rather than filling in four text boxes, the user speaks naturally while the relay streams audio and transcript events, asks follow-up questions, and builds story candidates during the session.

  • Live voice relay: WebSocket-based real-time transcript streaming during the conversation, with connection state management and automatic reconnection
  • Active story construction: spoken narrative is turned into candidate STAR stories, with follow-up prompts and post-session review hooks
  • Transcript windowing: long conversations are managed with a rolling context window that maintains the most relevant context while staying within token limits, preventing context overflow without losing the thread
  • Session persistence: transcripts are saved to Firestore copilotSessions; locally cached story drafts expire after 2 hours; the live relay itself warns at 8 minutes and closes at 9 minutes by default
  • Post-session feedback: optional ratings and notes captured after each companion session to improve the experience

The result: a user can talk through an interview story in their own words and leave with structured story material, linked responses, and optional AI review, without starting from a blank text area.

Priorities & Focus Sessions

Candidates define their target roles and use Pomodoro-style focus sessions to stay on track. Priorities feed into AI suggestions and activity insight signals.

  • Priority list with AI-guided reflection
  • Role-fit assessment against applications
  • Configurable Pomodoro timer with intention tracking
  • Post-session reflection (feeling, completion status)
  • Activity logging feeds the intelligence engine

4.2 AI-powered features

AI Flow Layer

The src/ai/flows directory currently contains 17 tested flow modules. Most use templated prompts, Zod-validated outputs, and provider routing; adjacent AI-backed features such as JD coverage, CV ranking, priority reflection, and role-fit assessment are implemented in route/service layers rather than this folder.

Profile Builder

A tailored CV/profile builder that uses the Experience Record and AI to assemble role-specific content with drag-and-drop section management, template selection, and application context linking.

4.3 Relationship management

Networking CRM & Next Action Engine

A relationship management system for professional contacts. Each contact is classified by type (former colleague, mentor, recruiter, alumni, warm introducer) and warmth level (new connection → acquaintance → warm → close). An intelligent Next Action Engine suggests who to reconnect with, based on:

  • Reminder due dates set by the user
  • Long gaps in contact, with thresholds that vary by warmth level (a close contact warrants faster follow-up than an acquaintance)
  • Never-contacted contacts that may be worth reaching out to
  • Warmth-appropriate intent suggestions (reconnect vs. ask for perspective vs. request introduction)

4.4 Advisor & collaboration

Advisor Dashboard

Career advisors see a unified view of their clients with status indicators, activity sparklines, and consent-gated access to detailed data.

Messaging & Pinned Recommendations

Thread-based 1-on-1 messaging between advisor and client. Advisors can also pin resources, events, and recommendations directly to a client’s hub, surfacing curated content alongside the client’s own workspace. This extends the collaborative model beyond passive data viewing into active guidance.

4.5 Public profiles & discovery

Public Candidate Profiles (Interrogable)

Candidates can share their profile publicly via a unique slug. The public profile includes an AI-generated orientation summary synthesised from the candidate’s full data. Visitors (including recruiters) can interact with the profile through:

  • Interrogable AI chat: ask questions about the candidate’s experience in natural language, with responses grounded in the Experience Record
  • Mock role assessments: generate a JD-aligned fit assessment (core, adjacent, and stretch roles) against the candidate’s profile
  • Public JD assessment: paste a job description and receive a structured coverage analysis against the candidate
  • Conversation turn limits and enable/disable controls give the candidate full control over public visibility

Candidate Search (Vector/Semantic)

Admin candidate search uses Firestore vector search with cosine distance on candidate embeddings. Admins can query candidates by job description text (100–15,000 characters), which is embedded and matched against stored candidate vectors. This enables semantic discovery: finding candidates whose experience is conceptually relevant, not just keyword-matched.

4.6 Onboarding & guidance

Onboarding Intake & Workspace Hub

New users are guided through a multi-step intake flow capturing their job search stage, career challenges, role intent, and targeted roles. This feeds the onboarding checklist, a progress tracker that highlights which features to explore next. The workspace hub surfaces contextual cards based on user role, assignment status, and feature state: advisors see client cards, candidates see activity and resources, and pinned recommendations appear for users with assigned advisors.

Teaching Moments

The platform follows a UX philosophy of “teach only at moments of action, no tutorials, no guides.” One-time dismissible messages appear at key moments (first reminder set, first intent viewed, first gap suggestion) stored in localStorage with no server round-trip. The user learns by doing, not by reading.

4.7 Platform & integration

Chrome Extension

One-click job capture from job boards with offline-first architecture. Applications are queued in chrome.storage.local and synced to the API with retry, exponential backoff, and failure classification. The extension also keeps local history and dedupe fingerprints.

Email Integration

OAuth2 flow for Outlook/Gmail. Emails are automatically classified (confirmation, interview, rejection, offer, recruiter contact) with entity extraction and confidence-scored application matching.

Admin Tooling

A comprehensive admin layer for system health and operations:

  • AI usage dashboard: token costs by feature, provider, and organisation
  • Credit management: balance view, top-ups, enrolment, and usage monitoring
  • System logs: unified view merging activities and system logs with filtering, pagination, and export
  • Design system audit: component inventory, token registry, and usage statistics
  • Test inventory: auto-populated listing of Vitest suites and case counts
  • Script runner: JSON-based configuration scripts for system setup tasks
  • Diagnostics: Firebase Storage connectivity validation
  • GenAI routing config: system-wide and per-organisation provider overrides with model validation

AI Architecture

The AI subsystem is one of the most complex parts of the application. Rather than calling a single provider directly, the tested AI flow modules and several AI-backed routes use a configurable provider layer with intent-based model selection, cost tracking, and structured output validation.

Multi-provider gateway

AI Flow
Gatewayintent routing
ProviderGemini / OpenAI / Anthropic
Validated Output

Intent-based routing

Each AI flow declares an intent that determines which model class to use. Intents are extensible and the system is not limited to a fixed set. Examples include:

IntentUse caseTypical provider
fastQuick responses, lower costGemini Flash, GPT-4o mini
thinkingDeep analysis, complex reasoningGemini Pro, Claude, GPT-4o

Routing configuration is stored in Firestore, enabling dynamic provider changes without deployment. Organisations can override system-wide routing with their own provider keys.

Structured output validation

JSON-structured AI outputs in the flow layer are validated against Zod schemas before they are used. This keeps the typed flow modules defensive when model output is non-deterministic.

// Example: STAR Answer Review output schema
const AIReviewSchema = z.object({
  overallGrade: z.enum(['Excellent', 'Good', 'Fair', 'Draft', 'Error']),
  specificity: z.number().min(1).max(5),
  impact: z.number().min(1).max(5),
  relevance: z.number().min(1).max(5),
  feedback: z.string(),
  sectionFeedback: z.record(SectionReviewSchema),
  followUpQuestions: z.array(z.string()).max(3),
  competencyTags: z.array(z.string()).max(5),
});

Token cost tracking

Every AI call records token usage. The platform calculates the real provider cost and applies a multiplier to cover infrastructure alongside AI spend. The resulting amount is deducted from the user’s credit balance in pence, with a ledger entry for every transaction.

Prompt system

Prompts live in src/ai/prompts/ as templates with variable substitution ({{{varName}}}). System instructions define tone rules, output structure, and guardrails. Some prompts embed example output blocks or strict JSON structure examples to keep responses consistent.

Real-time voice relay

The Story Companion uses a WebSocket-based live relay for real-time transcript streaming during voice conversations. This presents specific engineering challenges:

Candidate embeddings & vector search

Candidate summaries are embedded into vector representations stored in Firestore’s candidate_embeddings collection. Cloud Functions regenerate embeddings after Experience Record updates driven by CVs, STAR answers, and application materials. The admin candidate search uses Firestore’s findNearest() with cosine distance to find semantically similar candidates from job description text.

Credit & cost model

The platform has real running costs. Rather than a monthly subscription, users pay for credits, a model chosen because it is fairer: people pay in proportion to what they use, and charities can fund their own clients directly rather than managing seat licences.

The cost calculation is straightforward: every AI call records tokens in and out, the actual provider cost is calculated, and a platform multiplier is applied to cover infrastructure. Credits are tracked in pence (e.g. £5.00 = 500p) and deducted per use, with a full ledger of every transaction.

A small monthly minimum (~£2) is deducted from inactive accounts so unused balances don’t sit indefinitely. Accounts are never turned off. Without credit, AI-powered features become unavailable and the experience is effectively read-only.

Usage tracking is live; enforcement is the next commercial milestone.

Data Model

Firestore schema

Data is organised into root collections with user-scoped subcollections:

Collection pathPurpose
users/{uid}User profile, preferences, credit balance and status
users/{uid}/cvs/{cvId}CV documents with processing status, linkage, and AI feedback
users/{uid}/applications/{applicationId}Job applications with lifecycle data, materials, and interviews
users/{uid}/starResponses/{responseId}STAR answers with transcript and review data
users/{uid}/experience/recordExperience Record root document
users/{uid}/experience/record/nodes/{nodeId}Experience nodes, with nested items and variations
users/{uid}/activities/{activityId}Activity timeline events
users/{uid}/pomodoro/{sessionId}Focus sessions with intentions and reflections
users/{uid}/contacts/{contactId}Networking CRM contacts
users/{uid}/networking/{communicationId}Logged networking communications and follow-up context
users/{uid}/priorities/user-priorities-v3Current priorities document
users/{uid}/builderProfiles/{profileId}Builder profiles and assembled sections
users/{uid}/copilotSessions/{sessionId}Story Companion session state and transcript persistence
candidate_embeddings/{uid}Vector embeddings for semantic candidate search
organisations/{orgId}Organisation settings and theme
organisations/{orgId}/connections/{uid}Advisor-client relationship, consent mirror, and summary fields
system-settings/{docId}System routing, credit model, and related configuration documents

Experience Record hierarchy

Three-level structure: Node (role/education/skill) → Item (achievement/responsibility) → Variation (phrasing + source + context). Variations preserve how the same achievement has been described across CVs, STAR answers, cover letters, and voice sessions. See the narrative for the product rationale.

Sources feed in automatically via Cloud Functions:

CV Upload Extract Experience Record
STAR Answer Process Experience Record
Application Material Extract Experience Record
Voice Companion Structured Output Experience Record

Key domain types

The type system spans 45+ type files. Key domains include:

DomainKey types
STARSTARAnswer, AIReview, STARSectionReview, Question, CompetencyCategory
ApplicationsJobApplication, ApplicationMaterial, InterviewRound, Communication, JdExtractionResult
CV & ExperienceCV, ExperienceRecord, ExperienceNode, ExperienceItem, ExperienceVariation
ConsentConsentScope, ConsentSummary, ConsentAuditEntry, AdvisorAssignment
ActivityActivity, ActivitySummary, RankedSignal, DismissedSignal
NetworkingContact, ContactType, ContactWarmth, NextAction
CreditsSupportPackState, SupportPackLedgerEntry, SupportPackStatus

Advisor & Collaboration

The advisor system transforms myTrajectory from a solo tool into a collaborative platform. Advisors see a unified dashboard of their client roster with consent-gated access to detailed data.

Client roster

Each client card shows:

Consent model

Advisors can only view client data that the client has explicitly consented to share. Three granular scopes control access (see Trust & Collaboration for the design rationale):

ScopeGrants access to
viewActivityTimelineActivity feed: CV uploads, STAR completions, focus sessions, application updates
viewGoalsPriorities and target roles
viewApplicationsJob applications, materials, and interview schedule

Consent enforcement

Consent is enforced at the API route level, not the client. Every advisor-facing API route calls requireConsent(caller, targetUserId, scope) before returning data. A 5-minute in-memory cache (hasConsent()) balances freshness against Firestore read costs.

All consent changes are audit-logged with actor, action, and before/after state.

Pinned content & recommendations

Advisors can recommend specific resources, events, and content directly to individual clients. Pinned items appear on the client’s workspace hub alongside their own data, with status tracking (pending/done). This extends the advisor role from passive observer to active guide.

Connection summaries

To avoid expensive cross-user queries, Cloud Functions maintain denormalised ConnectionSummary documents that cache activity metrics, streak data, and sparkline arrays. Fields include lastActivityAt, activityCount7d, activityCountPrev7d, recentApplicationCount, currentStreak, and a weeklySparkline[] array. These update on every activity write via Firestore triggers, with a scheduled reconciliation job to correct any drift.

Activity Intelligence

The Activity Intelligence engine is a signal detection system that analyses recent behaviour across multiple rolling windows and surfaces actionable insights. It detects 13 distinct signals across momentum, gaps, and misalignment.

How it works

  1. Gather: gatherSignalData fetches activity history, applications, STAR answers, focus sessions, and dismissed signals from Firestore.
  2. Detect: detectActiveSignals is a pure function that evaluates all 13 signal conditions against the gathered data. No Firestore calls, fully testable.
  3. Rank: signals are prioritised: problem signals outrank positive signals; re-engagement fires alone.
  4. Display: ranked signals are shown to the user with contextual copy (different messaging when an advisor is present).
  5. Dismiss: users can dismiss signals; dismissed signals have a 14-day cooldown before they can fire again.

Signal inventory

Signal IDMeaning
general-re-engagement-after-long-gapUser has returned after a long inactive period
interview-prep-gap-before-knownAn interview is approaching but prep activity is missing
interview-reached-sparse-storiesInterview activity exists but the STAR library is still thin
drop-off-after-strong-startEarly engagement was strong but recent activity has fallen away
first-meaningful-milestoneThe user has reached an early meaningful milestone
consistent-weekActivity is spread consistently across the current week
meaningful-momentumRecent activity is up against the previous week
stale-applicationsApplications are sitting without recent progress
priority-misalignmentApplications do not line up with stated priorities
applications-no-priority-assessmentApplications exist without enough priority assessment context
scattered-applicationsApplication effort is spread thinly across different directions
tunnel-visionSearch behaviour is overly narrow relative to recent activity
high-intensityRecent focus-session intensity is unusually high

Engineering note: The signal detection function (detectActiveSignals) is deliberately pure: it takes data in and returns signals out. All Firestore interaction happens in the separate gatherSignalData function. This separation makes the detection logic fully unit-testable; the current service test file covers 13 signal conditions plus ranking, dismissal, snooze, and helper behaviour across 68 cases.

Security Patterns

Security is enforced at every layer, not assumed at any.

Authentication & authorisation

API route pattern

Many sensitive API routes follow a common defensive pattern built from authentication, validation, rate limiting, idempotency, and service-layer execution:

export async function POST(request: NextRequest) {
  // 1. Authenticate and authorise
  const user = await requireVerifiedUser(request);

  // 2. Validate request body against Zod schema
  const body = await validateBody(request, CreateApplicationSchema);

  // 3. Enforce rate limits
  await rateLimitOrThrow(user.uid, 'create-application');

  // 4. Check idempotency (prevent duplicate operations)
  await checkIdempotency(request, user.uid);

  // 5. Execute business logic via service layer
  const result = await applicationService.create(user.uid, body);

  // 6. Return typed response
  return NextResponse.json(result, { status: 201 });
}

Firestore rules

Security rules enforce access at the database level as a second line of defence:

Rate limiting

Token-bucket algorithm with Firestore-backed state and in-memory fallback. Per-user, per-feature granularity. Graceful degradation if Firestore is unavailable.

Data encryption

Sensitive fields (provider API keys, OAuth tokens) are encrypted at rest using AES-256-GCM via DATA_ENCRYPTION_KEY_CURRENT, with decryption fallback through DATA_ENCRYPTION_KEY_OLD_* during key rotation.

OAuth hardening

The email integration OAuth flow (Google and Microsoft) uses cryptographic state tokens to prevent CSRF attacks. Each authorisation request generates a 256-bit random state token stored in Firestore with a 10-minute TTL. On callback, the token is validated, checked for expiry, and immediately deleted to prevent replay. Callbacks that arrive without an email address from the provider are rejected outright.

Content Security Policy

A strict Content Security Policy controls which scripts, styles, connections, and frames the browser is allowed to load. The policy defaults to self for all resource types and explicitly allowlists only the external origins the platform depends on (Firebase, Google APIs, Sentry, and analytics). Plugins are disabled entirely, forms cannot submit to external sites, and the application cannot be iframed by third parties. Standard hardening headers (HSTS with preload, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) are set alongside the CSP.

Idempotency

Request deduplication prevents double-deduction of credits and duplicate record creation. Crucial for unreliable network conditions and retry scenarios.

Feature flags

Environment-variable-based feature toggles control rollout of new capabilities (live voice relay, AI example finder, AI STAR review). A context provider and useFeatureFlag hook give components clean access to flag state. This enables gradual rollout and safe experimentation without deployment risk.

Request ID tracking

Every client request generates a unique ID via createClientRequestId() that is propagated through the server via getRequestIdFromRequest(). This correlates logs, errors, and metrics across client and server boundaries, making it possible to trace a single user action through the entire stack in Sentry and system logs.

Cloud Functions

Cloud Functions handle asynchronous processing that should not block user interactions. They fall into two categories: Firestore triggers (react to data changes) and scheduled jobs (run on a cadence).

Firestore triggers

FunctionTriggerPurpose
processCvOnUploadCV document written / processing requestedExtract text, run 3-pass AI analysis, store feedback
processExperienceRecordOnCvExtractCV extraction completeAuto-populate Experience Record from CV content
processExperienceRecordOnStarAnswerSTAR answer savedAdd story achievements as experience variations
processExperienceRecordOnApplicationMaterialMaterial uploadedExtract experience claims from application materials
cleanupExperienceRecordOnCvDeleteCV deletedRemove CV-linked Experience Record content
syncCustomClaimsOnProfileChangeUser profile updatedPropagate role and organisation changes to Firebase Auth tokens
syncConnectionSummaryOnActivityWriteActivity document writtenUpdate cached advisor dashboard summaries
syncConnectionPermissionsOnConsentWriteConsent changedMirror consent status and permissions into organisation connection docs
syncConnectionProfileSnapshotOnUserWriteUser profile updatedSync denormalised profile fields into connection records

Scheduled jobs

JobCadencePurpose
cleanupExpiredSystemLogsEvery 24 hoursRetention management for system logs
syncUserEmailsEvery 30 minutesFetch and classify new emails from connected accounts
sendEventRemindersEvery 30 minutesSend reminders for upcoming events
chargeMonthlyBaselineMonthlyApply minimum monthly charge for accounts below the usage floor
updateSupportPackBurnRatesDaily at 03:0030-day rolling burn rate calculation
reconcileConnectionSummariesDaily at 03:00Correct out-of-sync advisor dashboard data

Domain event bus

Firestore triggers handle data-level reactions, but some actions need coordination across multiple concerns at once: logging an activity, creating an in-app notification, and sending an email should all happen when an advisor is assigned, but none of them belongs inside the trigger itself. A domain event bus handles this by decoupling the action from its consequences.

When a meaningful action occurs, the originating code publishes a single domain event. Registered subscribers (currently an event store, an activity logger, a notification writer, and an email sender) each decide independently whether to act on it. Subscribers declare themselves as either critical (failure rolls back the publish) or best-effort (failure is logged but does not block). The email subscriber, for example, runs as best-effort: a failed email should never prevent an advisor assignment from completing.

Because the same HTTP request can be retried, the bus supports subscriber-level idempotency. Each event generates a deterministic document ID from its type, entity, and request ID. Before a subscriber executes, the bus checks whether that subscriber has already processed the event. This prevents duplicate emails and duplicate activity entries without requiring callers to manage deduplication themselves.

Transactional email

Outbound email is handled by Resend, triggered via the email subscriber on the domain event bus. Events such as advisor assignment, consent changes, resource recommendations, event invitations, and new messages each produce a corresponding email to the affected party. Multi-recipient events are sent in parallel using Promise.allSettled() so that one failed delivery does not block the rest.

Users control what they receive through a two-level preference model: a global kill switch and per-type opt-outs stored on the user profile. New accounts start with all notifications enabled. The preference check is fail-open: if the Firestore lookup fails, the email is sent rather than silently dropped, on the basis that a duplicate is less harmful than a missing notification.

Every email includes List-Unsubscribe and List-Unsubscribe-Post headers conforming to RFC 8058, enabling one-click unsubscribe in supporting mail clients. Delivery outcomes are written to system_logs for admin visibility.

Design System

Component architecture

The UI is built on a three-layer component architecture:

1Radix UI PrimitivesUnstyled, accessible, composable
2shadcn/ui ComponentsStyled with Tailwind, 56 reusable UI component files
3Domain ComponentsFeature-specific compositions

Component inventory

The shared UI layer currently contains 56 component files in src/components/ui, while generated design-system stats track 34 audited shared/domain components used across 278 files.

Design tokens

A feature page token registry (feature-page-token-registry.ts) tracks all design tokens used across feature pages, with auto-generated statistics to prevent token sprawl. Tokens cover colour, spacing, typography, and dark mode overrides.

Responsive & accessible

Testing & Quality

Test runner

Vitest (not Jest) is the main test runner. Tests live in tests/ mirroring the src/ structure, with Playwright covering critical-path browser journeys under tests/e2e.

Test strategy

LayerApproachExamples
ServicesUnit tests with mocked FirestoreActivity insight detection (68 cases), application CRUD, consent logic
AI flowsPrompt validation, schema conformanceSTAR review output structure, JD extraction format
Server utilitiesAuth guards, rate limiting, validationConsent enforcement, role checks, Zod schema validation
ComponentsComponent behaviour testsForm submission, state transitions
E2EPlaywright for key user journeysCV upload, application creation, STAR recording

Testing patterns