User Journeys

Key workflows across all DIAL user personas

👤
Unauthenticated
Visitors who have not yet signed in or received an invite
  • Can view the sign-in page
  • Can reset password via email link
  • Can join an org via direct invite link only
  • Redirected to dashboard once authenticated
🧑‍💻
Member
Authenticated team members who participate in DORA capability assessments
  • Joins an organization via invite link (no self-sign-up)
  • Takes and reviews capability assessments
  • Views radar charts and AI recommendations
  • Explores capabilities via AI-guided conversation (when enabled)
  • Exports assessment reports to PDF
🛡️
Admin
Organization administrators who manage teams, members, and org-level analytics
  • Creates organizations and teams
  • Invites and manages member roles
  • Views org-level aggregate insights
  • Compares team scores side-by-side (when enabled)
  • Has all Member capabilities plus admin features
🔐

Authentication & Onboarding

Getting into DIAL — invite-only sign-up, sign-in, and joining an organization

1
Land on sign-in page
User arrives at the application. noAuthGuard redirects already-authenticated users directly to the dashboard. There is no public sign-up form — users may only join DIAL via an invite link.
/auth/signin
2
Receive and open an invite link
An Admin creates an invite for the member's email address. The invite link (/invite/:token) is the only way to create a new account. The token is also the Firestore document ID in the invites collection.
/invite/:token
3
Complete invite sign-up
The invite page pre-fills the member's email from the invite document. The member sets a password and submits. On success: a Firebase Auth account is created, a userProfiles document is written (linked to the invite via inviteId), and the invite's status is updated to accepted. Each invite can only be used once — already-accepted invites show an "Invalid Invite" error.
⚡ Security: Firestore rules enforce that the new profile must match the invite's email, organization, and pending status. Members cannot escalate their own role or change their organizationId after joining.
4
Redirect to dashboard
authGuard confirms authentication and the user lands on their role-appropriate dashboard — member view or admin view depending on their isAdmin flag.
/dashboard
5
Sign in (returning users)
Returning members authenticate via email and password on the sign-in page. Password reset is available via email link. There is no "Sign Up" tab — account creation is invite-only.
/auth/signin  |  /auth/reset
📋

Assessment Lifecycle

Creating, completing, and reviewing a DORA capability assessment

1
Start a new assessment
Admin or member initiates a new assessment from the dashboard. The assessment is created in Firestore with status: active and linked to the org.
/assessment/new
2
Work through the capability wizard
Team members rate each of the 20 capabilities (Emerging / Advancing / Leading) and provide qualitative notes about their current practices. Progress is saved to Firestore as capabilityResponses. The AI chat panel is shown only when the enable_ai_chat feature flag is enabled.
/assessment/:id/capabilities
3
AI generates assessments per capability
For each capability, the assessCapability Cloud Function sends the team's responses to Claude Sonnet. The AI returns a maturity assessment grounded in DORA research.
🤖 Claude Sonnet processes capability-specific context in up to 3 conversational turns. The turn budget is managed server-side in Cloud Functions.
4
Review summary and radar chart
The assessment summary page shows a radar chart of scores across all 4 categories, a composite maturity badge, and per-capability AI assessments. Scores are stored in assessmentResults.
/assessment/:id/summary
5
Explore recommendations
generateRecommendations produces prioritized improvement recommendations for each capability. Members can drill into any capability for deeper AI guidance.
/assessment/:id/results
6
Export to PDF
The full assessment report — scores, AI assessments, and recommendations — can be exported as a formatted PDF via PdfExportService (jsPDF + autotable).
🏢

Organization & Team Management

Admin-only: setting up the org, teams, and inviting members

1
Create organization
Admin creates an organization with name and settings. A unique invite code is generated automatically and stored in the organizations collection.
2
Create teams
Admin subdivides the org into teams (e.g., Platform, Product, QA). Teams are stored in the teams collection and displayed in org dashboards.
3
Invite members
Admin creates an invite for a specific email address. The resulting invite link (/invite/:token) is shared with the new member. The invite document ID is the token itself, stored in the invites collection with status: pending and an expiry timestamp. Each invite is single-use — once accepted it cannot be reused.
4
Manage member roles
Admin can view all members, update roles (member → admin), and assign members to teams. User profiles store isAdmin flag and orgId. Only admins may change a member's role, organization, or inviteId — members can only update their own safe fields (display name, preferences).
5
Monitor org insights
Admin dashboard shows org-level aggregate scores, AI insights generated by generateInsights, and assessment history across all teams.
/org/insights
📊

Dashboard & Analytics

Viewing assessment results, radar charts, and org health at a glance

📈 Radar Chart
Chart.js radar visualization shows capability scores across all 4 categories. Each axis represents a category aggregate score.
🏅 Composite Score
An overall maturity badge (Emerging / Advancing / Leading) is calculated from the weighted average of all 20 capability scores.
📋 Per-Capability Drill-Down
Click any capability on the radar or results table to see the AI assessment, team responses, and recommendations in detail.
🔍 Org-Level Insights
Admins see AI-generated strategic insights synthesizing patterns across all team assessments. Stored in orgInsights collection.
⚖️

Team Comparison

Side-by-side radar charts comparing capability scores across teams — controlled by the enable_compare feature flag

1
Select teams to compare
Admin selects 2 or more teams from the org. The compare view loads assessment results for each selected team. The compareGuard redirects to /dashboard if the enable_compare flag is disabled.
/compare
2
View side-by-side radar charts
Each team's scores are rendered on overlapping or adjacent radar charts. Score differences are highlighted to surface the largest gaps between teams.
3
Review AI cross-team insights
AI insights highlight systemic patterns and capability gaps that appear across multiple teams, surfacing org-wide improvement opportunities.
🗓️

Assessment History

Viewing past assessments and resuming with pre-populated responses

1
Browse past assessments
History page lists all completed assessments for the org, sorted by date. Each entry shows the composite score and assessment date.
/history
2
Review a past assessment
Drill into any historical assessment to see the full radar chart, capability scores, AI assessments, and recommendations as they were at the time.
3
Copy responses for a new assessment
A previous assessment's capabilityResponses can be copied as a starting point for a new assessment, so teams only update what has changed rather than starting from scratch.
💡 This enables longitudinal tracking — teams can see how their capability maturity evolves across multiple assessment cycles.
🤖

AI-Guided Assessment Conversation

Exploring a capability in depth with Claude Sonnet — controlled by the enable_ai_chat feature flag

1
Open a capability for deeper exploration
From the assessment results or dashboard, a member selects a capability to explore via AI chat. The chatAboutRecommendations Cloud Function is invoked. The AI chat panel is only shown when the enable_ai_chat Remote Config flag is enabled; the initial greeting message is also only sent when the flag is active.
2
Ask questions across 3 turns
The member can ask follow-up questions about the AI's assessment or recommendations. Each exchange is one of a fixed 3-turn budget per capability session.
🤖 Turn budget: 3 turns per capability, tracked server-side. Claude Sonnet is aware of the remaining turns and adjusts its responses to maximize usefulness within the budget.
3
Receive contextual recommendations
The AI grounds its guidance in the team's specific responses, current maturity level, and DORA research. Recommendations are practical and scoped to the team's context.
4
Budget exhausted — graceful wrap-up
When the turn budget is exhausted, Claude summarizes the key actions and directs the member back to the full results view for their remaining capabilities.
🚩

Feature Flag System

Firebase Remote Config controls feature availability — no redeployment needed to toggle features

1
FeatureFlagService bootstraps on app start
FeatureFlagService is provided at the root level. On construction it calls fetchAndActivate() against Firebase Remote Config, then updates Angular signals (enableAiReports, enableCompare, enableAiChat). Flags re-fetch every 10 seconds.
2
Flags drive UI visibility and route guards
Nav links for Compare and AI Reports are conditionally rendered with *ngIf="featureFlags.enableCompare()" / *ngIf="featureFlags.enableAiReports()". Route-level compareGuard and aiReportsGuard redirect to /dashboard if a disabled route is accessed directly.
3
Defaults keep app functional offline
All three flags default to true in FLAG_DEFAULTS. If Remote Config is unreachable, the app falls back to these defaults — all features remain available. Defaults are also set as remoteConfig.defaultConfig for SDK-level fallback.
💡 To add a new flag: add it to FLAG_DEFAULTS in feature-flag.service.ts, expose a signal, and create the matching parameter in the Firebase Remote Config console.
🔒

Firestore Security Model

Profile-gated access — having a Firebase Auth account is not enough to read data

1
Profile is the real membership check
Firestore rules use a hasProfile() helper — not just isAuthenticated() — to guard most collections. A Firebase Auth account alone does not grant data access; a userProfiles document must exist for the user. Profiles are only created by invite or by an admin, making the profile the effective membership gate.
2
Invite-validated profile creation
The isInvitedMemberProfile() rule function validates all of the following before allowing a new profile to be written: the Auth UID matches, the email matches the invite and the Firebase Auth token, the invite exists and is pending and unexpired, the organizationId matches the invite, and the role is member with isAdmin: false.
3
Members cannot escalate their own privileges
Profile updates by the owning user are allowed only if they do not touch privileged fields (uid, email, role, isAdmin, organizationId, inviteId). Only admins may change these fields. This is enforced by the changesOnlyOwnSafeFields() rule function.
4
Invites are single-use and token-addressed
The invite document ID is the token itself (not a separate field), so knowing the URL is sufficient to fetch the invite — no listing required. Public get is allowed; list is admin-only. Only the invited person (matching email + pending status) may flip status to accepted — and only that one field pair (status, acceptedAt) may change.
↑