Talent Activity
The talent activity system provides an audit trail of all talent lifecycle events. Activities are logged automatically via domain events and can also be created manually (e.g., recruiter notes).
Architecture
Domain services emit events via @nestjs/event-emitter. The TalentActivityListener captures these events and persists TalentActivity records. A DataLoader prevents N+1 queries when resolving activities on talent objects.
Cross-service events: Skill activities (SKILL_ADDED) are written directly to the TalentActivity table by background-tasks (Python/AsyncPG) since skills are extracted during CV parsing pipelines. The backend listener handles all other 20 event types.
Activity Types
| Type | Trigger |
|---|---|
TALENT_CREATED | New talent profile created |
TALENT_UPDATED | Profile fields modified |
TALENT_DELETED | Talent removed |
TALENT_VIEWED | Profile viewed by a user |
CV_UPLOADED | CV file uploaded |
CV_UPDATED | CV replaced or edited |
CV_DELETED | CV removed |
CV_PARSED | AI parsing completed |
ADDED_TO_POOL | Talent added to a pool |
REMOVED_FROM_POOL | Talent removed from a pool |
POOL_CHANGED | Talent moved between pools |
TALENT_SHARED | Shared with user or company |
TALENT_SHARE_REVOKED | Share access revoked |
CANDIDATE_CREATED | Added as job candidate |
CANDIDATE_STATUS_CHANGED | Candidate status updated |
CANDIDATE_STAGE_CHANGED | Moved to different pipeline stage |
NOTE_ADDED | Recruiter note added |
SKILL_ADDED | Skill added to talent (emitted by background-tasks) |
SKILL_REMOVED | Skill removed from talent (not yet implemented) |
EXTERNAL_SHARE_CREATED | External share link generated |
EXTERNAL_SHARE_ACCESSED | External share link viewed |
HIGH_MATCH_DETECTED | High job match score detected |
GraphQL Queries
List Activities (Company-Scoped)
query {
talentActivities(
filter: {
activityTypes: [CV_UPLOADED, NOTE_ADDED]
talentId: "talent-uuid"
performedById: "user-uuid"
}
pagination: { skip: 0, take: 20 }
) {
activities {
id
activityType
description
metadata
createdAt
talent { id firstName lastName }
performedBy { id firstName lastName }
}
totalCount
hasMore
}
}
Talent Activity History
query {
talentActivityHistory(
talentId: "talent-uuid"
pagination: { skip: 0, take: 50 }
) {
activities { id activityType description createdAt }
totalCount
hasMore
}
}
Activities on Talent Object
query {
talent(id: "talent-uuid") {
id
firstName
activities(limit: 10) {
id activityType description createdAt
}
}
}
Manual Activity Creation
Recruiters can create manual activity entries (e.g., interview notes, status updates):
mutation {
createTalentActivity(input: {
talentId: "talent-uuid"
activityType: NOTE_ADDED
description: "Interview completed - strong technical skills"
metadata: { rating: 4, stage: "technical" }
}) { id activityType description createdAt }
}
mutation { deleteTalentActivity(id: "activity-uuid") }
Data Model
Each activity stores talentId, companyId, activityType, performedById, description, and optional metadata (JSON). Client context fields ipAddress and userAgent are captured when available.
Indexes: talentId, companyId, activityType, performedById, createdAt DESC, composites on (companyId, talentId), (companyId, activityType), (talentId, createdAt DESC).
Activities cascade-delete when the parent talent or company is removed. The performedById field is set to null if the performing user is deleted.