@adaptware/eagles-db 0.5.57 → 0.5.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -414,58 +414,62 @@ enum ApplicationOfferSubstatus {
414
414
 
415
415
  model Application {
416
416
  /// Primary key
417
- id String @id @default(uuid())
417
+ id String @id @default(uuid())
418
418
  /// Foreign keys
419
- organisation_id String
420
- candidate_id String
421
- resume_id String
422
- job_description_id String
423
- evaluation_plan_id String?
424
- recruiter_id String?
425
- source_id String?
419
+ organisation_id String
420
+ candidate_id String
421
+ resume_id String
422
+ job_description_id String
423
+ evaluation_plan_id String?
424
+ recruiter_id String?
425
+ source_id String?
426
426
  /// Data fields
427
- fit_check_score Float?
428
- applied_at DateTime @default(now())
429
- round_no Int @default(0)
430
- is_jaf_completed Boolean @default(false)
431
- proceed_to_next_round Boolean? @default(false)
432
- evaluationComment String?
427
+ fit_check_score Float?
428
+ applied_at DateTime @default(now())
429
+ round_no Int @default(0)
430
+ is_jaf_completed Boolean @default(false)
431
+ proceed_to_next_round Boolean? @default(false)
432
+ evaluationComment String?
433
433
  /// Candidate flag (orthogonal to ApplicationStatus). Latest actor/comment only.
434
- is_flagged Boolean @default(false)
435
- flag_comment String?
436
- flagged_at DateTime?
437
- flagged_by String?
438
- unflag_comment String?
439
- unflagged_at DateTime?
440
- unflagged_by String?
441
- fit_check_status FitCheckStatus?
442
- status ApplicationStatus @default(PENDING)
434
+ is_flagged Boolean @default(false)
435
+ flag_comment String?
436
+ flagged_at DateTime?
437
+ flagged_by String?
438
+ unflag_comment String?
439
+ unflagged_at DateTime?
440
+ unflagged_by String?
441
+ fit_check_status FitCheckStatus?
442
+ status ApplicationStatus @default(PENDING)
443
443
  /// Fine-grained offer-stage state. Meaningful only when status = OFFER (or was OFFER for a terminal-substatus that flipped to REJECTED / JOINED).
444
- offer_substatus ApplicationOfferSubstatus?
444
+ offer_substatus ApplicationOfferSubstatus?
445
445
  /// Audit fields
446
- created_at DateTime @default(now())
447
- updated_at DateTime @updatedAt
448
- created_by String
449
- updated_by String
450
- is_active Boolean @default(true)
446
+ created_at DateTime @default(now())
447
+ updated_at DateTime @updatedAt
448
+ created_by String
449
+ updated_by String
450
+ is_active Boolean @default(true)
451
+ fit_check_report_document_id String? @unique
452
+ fit_check_report_updated_flag Boolean @default(false)
453
+ fit_check_report_data Json? @default("{}")
451
454
  /// Relations
452
- evaluation_plan EvaluationPlan? @relation(fields: [evaluation_plan_id], references: [id])
453
- candidate User @relation(fields: [candidate_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
454
- organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
455
- resume Resume @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
456
- job_description JobDescription @relation(fields: [job_description_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
457
- fit_check FitCheck?
458
- recruiter User? @relation("ApplicationRecruiter", fields: [recruiter_id], references: [id])
459
- source Source? @relation(fields: [source_id], references: [id], onDelete: SetNull)
460
- interviews Interview[]
461
- assignments Assignment[]
462
- ongoing_evaluations OngoingEvaluation[]
463
- activity_logs ActivityLog[]
464
- notes Notes[]
465
- conversations Conversation[]
466
- communication_messages CommunicationMessage[]
467
- job_application_responses JobApplicationResponse[]
468
- offer Offer?
455
+ evaluation_plan EvaluationPlan? @relation(fields: [evaluation_plan_id], references: [id])
456
+ candidate User @relation(fields: [candidate_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
457
+ organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
458
+ resume Resume @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
459
+ job_description JobDescription @relation(fields: [job_description_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
460
+ fit_check FitCheck?
461
+ recruiter User? @relation("ApplicationRecruiter", fields: [recruiter_id], references: [id])
462
+ source Source? @relation(fields: [source_id], references: [id], onDelete: SetNull)
463
+ interviews Interview[]
464
+ assignments Assignment[]
465
+ ongoing_evaluations OngoingEvaluation[]
466
+ activity_logs ActivityLog[]
467
+ notes Notes[]
468
+ conversations Conversation[]
469
+ communication_messages CommunicationMessage[]
470
+ job_application_responses JobApplicationResponse[]
471
+ offer Offer?
472
+ fit_check_report_document Document? @relation("ApplicationFitCheckReportDocument", fields: [fit_check_report_document_id], references: [id], onDelete: SetNull)
469
473
 
470
474
  @@unique([candidate_id, job_description_id]) // Ensures candidate applies only once per job
471
475
  @@index([candidate_id])
@@ -552,6 +556,8 @@ model AssessmentLibrary {
552
556
  created_by String
553
557
  updated_by String
554
558
  is_active Boolean @default(true)
559
+ /// Frozen question plan, batch cursor, and bank-generation status (JSON).
560
+ generation_state Json?
555
561
  organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
556
562
  questions Question[] // One assessment → many questions
557
563
  assessments Assessment[]
@@ -1401,6 +1407,7 @@ model Document {
1401
1407
  offer_letters OfferLetter[] @relation("OfferLetterPdf")
1402
1408
  offer_letter_template_snapshots OfferLetter[] @relation("OfferLetterTemplateSnapshot")
1403
1409
  offer_draft_edited_html Offer[] @relation("OfferDraftEditedHtml")
1410
+ application_fit_check_report Application? @relation("ApplicationFitCheckReportDocument")
1404
1411
 
1405
1412
  // Indexes
1406
1413
  @@index([candidate_id])
@@ -1445,6 +1452,8 @@ model EvaluationCriteria {
1445
1452
 
1446
1453
  enum RoundType {
1447
1454
  ASSESSMENT
1455
+ GENERAL_APTITUDE_ASSESSMENT
1456
+ DOMAIN_KNOWLEDGE_ASSESSMENT
1448
1457
  INTERVIEW
1449
1458
  ASSIGNMENT
1450
1459
  SCREENING
@@ -1539,26 +1548,6 @@ model EvaluationPlan {
1539
1548
  @@schema("public")
1540
1549
  }
1541
1550
 
1542
- model Feedback {
1543
- id String @id @default(uuid())
1544
- interview_id String
1545
- order_no Int
1546
-
1547
- ai_feedback Json?
1548
- human_feedback Json?
1549
-
1550
- created_at DateTime @default(now())
1551
- updated_at DateTime @updatedAt
1552
- created_by String
1553
- updated_by String
1554
- is_active Boolean @default(true)
1555
- interview Interview @relation(fields: [interview_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1556
-
1557
- @@unique([interview_id, order_no])
1558
- @@index([interview_id])
1559
- @@schema("public")
1560
- }
1561
-
1562
1551
  enum FitCheckStatus {
1563
1552
  RECOMMENDED
1564
1553
  TO_REVIEW
@@ -1582,6 +1571,8 @@ model FitCheck {
1582
1571
  overall_score Float
1583
1572
  /// Free-text from Overall Analysis agent; preferred over recommendation Json.
1584
1573
  overall_recommendation String?
1574
+ /// Why `overall_score` is this value. Distinct from `overall_recommendation`.
1575
+ score_reasoning String?
1585
1576
  short_match_analysis Json[]
1586
1577
  fit_comparison Json?
1587
1578
  recommendation Json?
@@ -1970,18 +1961,21 @@ enum AiAssistanceMode {
1970
1961
  model Interview {
1971
1962
  id String @id @default(uuid())
1972
1963
  /// Foreign keys
1973
- organisation_id String
1964
+ organisation_id String?
1974
1965
  candidate_id String
1975
1966
  interviewer_id String?
1976
- evaluation_plan_id String
1967
+ evaluation_plan_id String?
1977
1968
  application_id String?
1978
1969
  resume_id String
1979
- job_description_id String
1970
+ job_description_id String?
1980
1971
  calendar_event_id String?
1972
+ calendar_events Json?
1973
+ /// True for candidate-initiated profile enrichment voice sessions (not hiring).
1974
+ candidate_initiated Boolean @default(false)
1975
+ /// Profile AI: purpose, focusAreas, focusAreaCoverage, etc.
1976
+ candidate_interview_metadata Json?
1981
1977
  /// Data
1982
1978
  room_name String?
1983
- /// @deprecated Prefer `ai_feedback.feedback` (narrative string on AI feedback JSON).
1984
- interview_summary String[]
1985
1979
  scheduled_start_time DateTime?
1986
1980
  scheduled_end_time DateTime?
1987
1981
  actual_start_time DateTime?
@@ -1992,20 +1986,22 @@ model Interview {
1992
1986
  recording_url String?
1993
1987
  recording_document_id String? @unique
1994
1988
  evaluation_type EvaluationType? @default(TREADSPACE_AI_ASSISTED)
1989
+ is_proctored Boolean @default(true) @map("is_proctored")
1995
1990
  ai_assistance_mode AiAssistanceMode? @default(ASSISTED)
1996
1991
  interview_status InterviewStatus @default(SCHEDULED)
1997
1992
  interview_platform InterviewPlatform @default(TREADSPACE)
1998
- overall_feedback String?
1999
1993
  /// Feedback JSON fields
2000
1994
  /// AI overall payload: `{ score, rating, feedback? }` where `feedback` is the
2001
- /// narrative summary string (replaces deprecated `interview_summary`).
1995
+ /// narrative summary string.
2002
1996
  ai_feedback Json?
2003
1997
  human_feedback Json?
2004
- section_feedback Json[]
2005
1998
  interview_preparation Json?
2006
1999
  final_feedback Json?
2007
2000
  interview_analysis_ranges Json?
2008
2001
  interview_flow_summary String?
2002
+ /// Extra Orion prose for downstream AI input (FitCheck, interview prep).
2003
+ /// Not interviewer-facing; omit from evidence GET / snapshots / Zoho.
2004
+ ai_context String?
2009
2005
  /// When Orion AI feedback was last persisted on this interview.
2010
2006
  ai_feedback_generated_at DateTime?
2011
2007
  /// Experimental Beta AI feedback store (parallel to production AI columns).
@@ -2014,6 +2010,7 @@ model Interview {
2014
2010
  beta_section_feedback Json[] @default([])
2015
2011
  beta_final_feedback Json?
2016
2012
  beta_interview_flow_summary String?
2013
+ beta_ai_context String?
2017
2014
  beta_ai_feedback_generated_at DateTime?
2018
2015
  /// When recruiter human feedback was last saved (draft or final).
2019
2016
  human_feedback_updated_at DateTime?
@@ -2021,44 +2018,48 @@ model Interview {
2021
2018
  evaluated_at DateTime?
2022
2019
 
2023
2020
  /// Self-scheduling
2024
- reschedule_count Int @default(0)
2021
+ reschedule_count Int @default(0)
2025
2022
  last_rescheduled_at DateTime?
2026
2023
  first_assigned_at DateTime?
2027
2024
  /// True while the candidate is waiting on recruiter action (typically a
2028
2025
  /// reassign) after they submitted alternate slots via "None match" /
2029
2026
  /// "Suggest times". Cleared whenever the recruiter routes a fresh
2030
2027
  /// request via `upsertInterviewRequestRecord`.
2031
- awaiting_recruiter_action Boolean @default(false)
2028
+ awaiting_recruiter_action Boolean @default(false)
2032
2029
  /// Audit fields
2033
- created_at DateTime @default(now())
2034
- updated_at DateTime @updatedAt
2030
+ created_at DateTime @default(now())
2031
+ updated_at DateTime @updatedAt
2035
2032
  created_by String
2036
2033
  updated_by String
2037
- is_active Boolean @default(true)
2034
+ is_active Boolean @default(true)
2035
+
2036
+ //interview proctoring
2037
+ proctoringSession ProctoringSession?
2038
+
2038
2039
  // Relations
2039
- candidate User? @relation("CandidateInterviews", fields: [candidate_id], references: [id], onDelete: SetNull)
2040
- interviewer User? @relation("InterviewerInterviews", fields: [interviewer_id], references: [id], onDelete: SetNull)
2041
- organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2042
- recording_document Document? @relation(fields: [recording_document_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2043
- job_description JobDescription @relation(fields: [job_description_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2044
- resume Resume @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2045
- evaluation_plan EvaluationPlan? @relation(fields: [evaluation_plan_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2046
- application Application? @relation(fields: [application_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2047
- ongoing_evaluation OngoingEvaluation?
2048
- transcripts Transcript[]
2049
- suggestions Suggestion[]
2050
- feedback Feedback[]
2051
- interviewNotes InterviewNotes[]
2052
- liveAnalysis LiveAnalysis[]
2053
- zoom_meeting ZoomMeeting?
2040
+ candidate User? @relation("CandidateInterviews", fields: [candidate_id], references: [id], onDelete: SetNull)
2041
+ interviewer User? @relation("InterviewerInterviews", fields: [interviewer_id], references: [id], onDelete: SetNull)
2042
+ organisation Organisation? @relation(fields: [organisation_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2043
+ recording_document Document? @relation(fields: [recording_document_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2044
+ job_description JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2045
+ resume Resume @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2046
+ evaluation_plan EvaluationPlan? @relation(fields: [evaluation_plan_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2047
+ application Application? @relation(fields: [application_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2048
+ ongoing_evaluation OngoingEvaluation?
2049
+ transcripts Transcript[]
2050
+ suggestions Suggestion[]
2051
+ interviewNotes InterviewNotes[]
2052
+ liveAnalysis LiveAnalysis[]
2053
+ zoom_meeting ZoomMeeting?
2054
2054
 
2055
2055
  // Indexes
2056
- @@unique([interviewer_id, candidate_id, evaluation_plan_id])
2057
2056
  @@index([candidate_id])
2057
+ @@index([candidate_id, candidate_initiated, created_at])
2058
2058
  @@index([interviewer_id])
2059
2059
  @@index([job_description_id])
2060
2060
  @@index([organisation_id, interview_status])
2061
2061
  @@index([interview_status, scheduled_start_time])
2062
+ @@index([candidate_initiated, interview_status])
2062
2063
  @@schema("public")
2063
2064
  }
2064
2065
 
@@ -2381,13 +2382,15 @@ model JobApplicationField {
2381
2382
  updated_by String
2382
2383
  created_at DateTime @default(now())
2383
2384
  updated_at DateTime @updatedAt
2385
+ /// order of question
2386
+ order_no Int? //nullable for now to allow migration
2384
2387
  /// Relations
2385
2388
  organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2386
2389
  job_application_template JobApplicationTemplate @relation(fields: [template_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2387
2390
  job_application_responses JobApplicationResponse[]
2388
2391
 
2389
2392
  /// Indexes
2390
- @@index([organisation_id, template_id])
2393
+ @@index([organisation_id, template_id, order_no])
2391
2394
  @@schema("public")
2392
2395
  }
2393
2396
 
@@ -2415,13 +2418,14 @@ model JobApplicationResponse {
2415
2418
  created_by String
2416
2419
  updated_by String
2417
2420
  is_active Boolean @default(true)
2421
+ order_no Int? //nullable for now to allow migration
2418
2422
  /// Relations
2419
2423
  application Application @relation(fields: [application_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2420
2424
  field JobApplicationField? @relation(fields: [job_application_field_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
2421
2425
  document Document? @relation(fields: [document_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2422
2426
 
2423
- /// Partial unique on (application_id, job_application_field_id) WHERE job_application_field_id IS NOT NULL — see migration SQL
2424
- @@index([application_id])
2427
+ // / Partial unique on (application_id, job_application_field_id) WHERE job_application_field_id IS NOT NULL — see migration SQL
2428
+ @@index([application_id, is_active, order_no])
2425
2429
  @@index([job_application_field_id])
2426
2430
  @@schema("public")
2427
2431
  }
@@ -2761,13 +2765,15 @@ model AiCostLedgerEntry {
2761
2765
  @@schema("licensing")
2762
2766
  }
2763
2767
 
2764
- /// Immutable assignment history for Hiring Unit department/job changes.
2768
+ /// Immutable assignment history for Hiring Unit department/user/job changes.
2765
2769
  model PurchasedHiringUnitAssignmentEvent {
2766
2770
  id String @id @default(uuid())
2767
2771
  purchased_hiring_unit_id String
2768
2772
  event_kind PurchasedHiringUnitAssignmentEventKind
2769
2773
  previous_department_id String?
2770
2774
  new_department_id String?
2775
+ previous_assigned_user_id String?
2776
+ new_assigned_user_id String?
2771
2777
  previous_job_description_id String?
2772
2778
  new_job_description_id String?
2773
2779
  effective_at DateTime @default(now())
@@ -2880,6 +2886,8 @@ enum PurchasedTopUpStatus {
2880
2886
  enum PurchasedHiringUnitAssignmentEventKind {
2881
2887
  DEPARTMENT_ASSIGNED
2882
2888
  DEPARTMENT_UNASSIGNED
2889
+ USER_ASSIGNED
2890
+ USER_UNASSIGNED
2883
2891
  JOB_LINKED
2884
2892
  JOB_UNLINKED
2885
2893
 
@@ -3001,6 +3009,8 @@ model PurchasedHiringUnit {
3001
3009
  status PurchasedHiringUnitStatus @default(AVAILABLE)
3002
3010
  /// Current department allocation (licensing allocation, not hiring manager).
3003
3011
  department_id String?
3012
+ /// Optional person reservation. Units with this set are consumed before dept/org pools.
3013
+ assigned_user_id String?
3004
3014
  /// Set once when department_id is first assigned; not cleared on reassignment.
3005
3015
  first_assigned_at DateTime?
3006
3016
  /// Optional unit expiry.
@@ -3017,6 +3027,7 @@ model PurchasedHiringUnit {
3017
3027
  organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
3018
3028
  purchased_license_package PurchasedLicensePackage @relation(fields: [purchased_license_package_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
3019
3029
  department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3030
+ assigned_user User? @relation("AssignedHiringUnits", fields: [assigned_user_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3020
3031
  job_description JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3021
3032
  assignment_events PurchasedHiringUnitAssignmentEvent[]
3022
3033
  ledger_entries LicensingLedgerEntry[]
@@ -3025,9 +3036,11 @@ model PurchasedHiringUnit {
3025
3036
  @@index([organisation_id])
3026
3037
  @@index([purchased_license_package_id])
3027
3038
  @@index([department_id])
3039
+ @@index([assigned_user_id])
3028
3040
  @@index([job_description_id])
3029
3041
  @@index([organisation_id, status])
3030
3042
  @@index([organisation_id, department_id])
3043
+ @@index([organisation_id, assigned_user_id])
3031
3044
  @@index([organisation_id, job_description_id])
3032
3045
  @@map("purchased_hiring_units")
3033
3046
  @@schema("licensing")
@@ -3331,58 +3344,61 @@ enum OngoingEvaluationRoundStatus {
3331
3344
 
3332
3345
  model OngoingEvaluation {
3333
3346
  // Primary Key
3334
- id String @id @default(uuid())
3347
+ id String @id @default(uuid())
3335
3348
  // Foreign Keys
3336
- organisation_id String
3337
- application_id String
3338
- candidate_id String
3339
- resume_id String
3340
- job_description_id String
3341
- evaluation_plan_id String
3342
- interview_id String? @unique
3343
- assignment_id String? @unique
3344
- assessment_id String? @unique
3349
+ organisation_id String
3350
+ application_id String
3351
+ candidate_id String
3352
+ resume_id String
3353
+ job_description_id String
3354
+ evaluation_plan_id String
3355
+ interview_id String? @unique
3356
+ assignment_id String? @unique
3357
+ assessment_id String? @unique
3345
3358
  // Data Fields
3346
- state OngoingEvaluationState? @default(NOT_STARTED)
3347
- round_status OngoingEvaluationRoundStatus? @default(NOT_SCHEDULED)
3348
- comments String? @default("")
3349
- completed_at DateTime?
3350
- assigned_at DateTime?
3351
- result_at DateTime?
3352
- is_committed Boolean?
3359
+ state OngoingEvaluationState? @default(NOT_STARTED)
3360
+ round_status OngoingEvaluationRoundStatus? @default(NOT_SCHEDULED)
3361
+ comments String? @default("")
3362
+ completed_at DateTime?
3363
+ assigned_at DateTime?
3364
+ result_at DateTime?
3365
+ is_committed Boolean?
3353
3366
  /// Evaluation Plan Fields Filled After Round Started
3354
- title String?
3355
- description String?
3356
- order_no Int?
3357
- round_purpose String?
3358
- focus_areas String[]
3359
- topics String[]
3360
- assignee String?
3361
- elimination_round Boolean? @default(false)
3362
- evaluation_type EvaluationType?
3363
- type_of_round RoundType?
3364
- duration Int?
3365
- deadline Int?
3366
- duration_unit DurationUnit?
3367
- details Json?
3368
- category EvaluationCategory?
3367
+ title String?
3368
+ description String?
3369
+ order_no Int?
3370
+ round_purpose String?
3371
+ focus_areas String[]
3372
+ topics String[]
3373
+ assignee String?
3374
+ elimination_round Boolean? @default(false)
3375
+ evaluation_type EvaluationType?
3376
+ type_of_round RoundType?
3377
+ duration Int?
3378
+ deadline Int?
3379
+ duration_unit DurationUnit?
3380
+ details Json?
3381
+ category EvaluationCategory?
3382
+ /// Round relevance vs JD / application. Unused until a later writer.
3383
+ relevance_score Float?
3384
+ relevance_reasoning String?
3369
3385
  // Audit Fields
3370
- created_at DateTime @default(now())
3371
- updated_at DateTime @updatedAt
3372
- created_by String
3373
- updated_by String
3374
- is_active Boolean @default(true)
3375
- is_current Boolean @default(false)
3386
+ created_at DateTime @default(now())
3387
+ updated_at DateTime @updatedAt
3388
+ created_by String
3389
+ updated_by String
3390
+ is_active Boolean @default(true)
3391
+ is_current Boolean @default(false)
3376
3392
  // Relations
3377
- jobDescription JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull)
3378
- application Application @relation(fields: [application_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3379
- candidate User? @relation(fields: [candidate_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3380
- resume Resume? @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3381
- organisation Organisation? @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3382
- evaluation_plan EvaluationPlan @relation(fields: [evaluation_plan_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3383
- interview Interview? @relation(fields: [interview_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3384
- assignment Assignment?
3385
- assessment Assessment? @relation(fields: [assessment_id], references: [id], onDelete: SetNull)
3393
+ jobDescription JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull)
3394
+ application Application @relation(fields: [application_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3395
+ candidate User? @relation(fields: [candidate_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3396
+ resume Resume? @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3397
+ organisation Organisation? @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3398
+ evaluation_plan EvaluationPlan @relation(fields: [evaluation_plan_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3399
+ interview Interview? @relation(fields: [interview_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3400
+ assignment Assignment?
3401
+ assessment Assessment? @relation(fields: [assessment_id], references: [id], onDelete: SetNull)
3386
3402
 
3387
3403
  @@unique([application_id, evaluation_plan_id])
3388
3404
  @@index([application_id])
@@ -3494,6 +3510,15 @@ enum OrganisationSize {
3494
3510
  @@schema("public")
3495
3511
  }
3496
3512
 
3513
+ /// Who may consume unused hiring units. Changing this does not rewrite unit rows.
3514
+ enum HiringUnitAssignmentScope {
3515
+ USER
3516
+ DEPARTMENT
3517
+ ORGANISATION
3518
+
3519
+ @@schema("public")
3520
+ }
3521
+
3497
3522
  model Organisation {
3498
3523
  id String @id @default(uuid())
3499
3524
  name String @unique
@@ -3536,6 +3561,8 @@ model Organisation {
3536
3561
  timezone String @default("Asia/Kolkata")
3537
3562
  /// ISO 3166-1 alpha-2 (e.g. IN).
3538
3563
  country_code String @default("IN")
3564
+ /// Who may consume unused hiring units. Does not rewrite unit rows when changed.
3565
+ hiring_unit_assignment_scope HiringUnitAssignmentScope @default(DEPARTMENT)
3539
3566
  organisation_configs OrganisationConfig[]
3540
3567
  activity_events ActivityEvent[]
3541
3568
  usage_ledger_entries UsageLedgerEntry[]
@@ -3665,6 +3692,133 @@ model PipelineDistributionAnalyticsDaily {
3665
3692
  @@schema("analytics")
3666
3693
  }
3667
3694
 
3695
+ enum RiskLevel {
3696
+ LOW
3697
+ MEDIUM
3698
+ HIGH
3699
+ CRITICAL
3700
+
3701
+ @@schema("public")
3702
+ }
3703
+
3704
+ enum AnalysisStatus {
3705
+ QUEUED
3706
+ RUNNING
3707
+ COMPLETED
3708
+ FAILED
3709
+
3710
+ @@schema("public")
3711
+ }
3712
+
3713
+ enum ProctoringEventType {
3714
+ GAZE_DISRUPTION
3715
+ OBJECT_DETECTED
3716
+ FACE_MISSING
3717
+ MULTIPLE_FACES
3718
+ BROWSER_TAB_SWITCH
3719
+ BROWSER_BLUR
3720
+ FULLSCREEN_EXIT
3721
+
3722
+ @@schema("public")
3723
+ }
3724
+
3725
+ enum ProctoringSessionStatus {
3726
+ NOT_STARTED
3727
+ CALIBRATING
3728
+ CALIBRATED
3729
+ PROCESSING
3730
+ COMPLETED
3731
+ FAILED
3732
+
3733
+ @@schema("public")
3734
+ }
3735
+
3736
+ model ProctoringSession {
3737
+ id String @id @default(uuid()) @map("id")
3738
+ interviewId String @unique @map("interview_id")
3739
+ interview Interview @relation(fields: [interviewId], references: [id], onDelete: Cascade)
3740
+
3741
+ candidateId String @map("candidate_id")
3742
+ candidate User @relation(fields: [candidateId], references: [id], onDelete: Cascade)
3743
+
3744
+ // Proctoring Lifecycle & Media
3745
+ isProctored Boolean @default(true) @map("is_proctored")
3746
+ status ProctoringSessionStatus @default(NOT_STARTED) @map("status")
3747
+ calibrationFeatures Json? @map("calibration_features")
3748
+ fullVideoKey String? @map("full_video_key")
3749
+ fullVideoUrl String? @map("full_video_url")
3750
+ chunkCount Int @default(0) @map("chunk_count")
3751
+ completedChunks Int @default(0) @map("completed_chunks")
3752
+
3753
+ // High-Level Aggregated Metrics for Downstream AI Evaluation
3754
+ overallRisk RiskLevel @default(LOW) @map("overall_risk")
3755
+ integrityScore Float @default(100.0) @map("integrity_score")
3756
+
3757
+ totalDurationSec Float @default(0.0) @map("total_duration_sec")
3758
+ faceDetectedPct Float @default(100.0) @map("face_detected_pct")
3759
+ onScreenGazePct Float @default(100.0) @map("on_screen_gaze_pct")
3760
+
3761
+ // Event Counters
3762
+ totalGazeEvents Int @default(0) @map("total_gaze_events")
3763
+ totalObjectEvents Int @default(0) @map("total_object_events")
3764
+ totalFaceEvents Int @default(0) @map("total_face_events")
3765
+ totalBrowserEvents Int @default(0) @map("total_browser_events")
3766
+
3767
+ createdAt DateTime @default(now()) @map("created_at")
3768
+ updatedAt DateTime @updatedAt @map("updated_at")
3769
+
3770
+ analyses ProctoringAnalysis[]
3771
+ events ProctoringEvent[]
3772
+
3773
+ @@index([candidateId])
3774
+ @@map("proctoring_sessions")
3775
+ @@schema("public")
3776
+ }
3777
+
3778
+ model ProctoringAnalysis {
3779
+ id String @id @default(uuid()) @map("id")
3780
+ sessionId String @map("session_id")
3781
+ session ProctoringSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
3782
+
3783
+ status AnalysisStatus @default(QUEUED)
3784
+
3785
+ chunkIndex Int @default(0) @map("chunk_index")
3786
+ totalChunks Int @default(1) @map("total_chunks")
3787
+ startTimeSec Float @default(0.0) @map("start_time_sec")
3788
+ endTimeSec Float @default(0.0) @map("end_time_sec")
3789
+
3790
+ videoKey String? @map("video_key")
3791
+ annotatedKey String? @map("annotated_key")
3792
+
3793
+ error String?
3794
+ createdAt DateTime @default(now()) @map("created_at")
3795
+ updatedAt DateTime @updatedAt @map("updated_at")
3796
+
3797
+ @@index([sessionId, chunkIndex])
3798
+ @@map("proctoring_analyses")
3799
+ @@schema("public")
3800
+ }
3801
+
3802
+ model ProctoringEvent {
3803
+ id String @id @default(uuid()) @map("id")
3804
+ sessionId String @map("session_id")
3805
+ session ProctoringSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
3806
+
3807
+ eventType ProctoringEventType @map("event_type")
3808
+ timestampSec Float @map("timestamp_sec")
3809
+ timestampStr String @map("timestamp_str")
3810
+
3811
+ risk RiskLevel @default(LOW)
3812
+ metadata Json?
3813
+
3814
+ createdAt DateTime @default(now()) @map("created_at")
3815
+
3816
+ @@index([sessionId, timestampSec])
3817
+ @@index([sessionId, eventType])
3818
+ @@map("proctoring_events")
3819
+ @@schema("public")
3820
+ }
3821
+
3668
3822
  enum AsyncOperationStatus {
3669
3823
  PENDING
3670
3824
  IN_PROGRESS
@@ -3716,12 +3870,24 @@ enum Difficulty {
3716
3870
  @@schema("public")
3717
3871
  }
3718
3872
 
3873
+ enum QuestionCategory {
3874
+ /// Aptitude, logical reasoning, numerical, verbal — non-domain general tests.
3875
+ GENERAL_APTITUDE
3876
+ /// Role/domain/technical knowledge (skills from JD and competencies).
3877
+ DOMAIN_KNOWLEDGE
3878
+
3879
+ @@schema("public")
3880
+ }
3881
+
3719
3882
  model Question {
3720
3883
  id String @id @default(uuid())
3721
3884
  assessment_library_id String?
3722
3885
  evaluation_plan_id String?
3723
3886
  candidate_id String?
3724
3887
  skill String?
3888
+ category QuestionCategory?
3889
+ /// Stable idempotency key for reusable bank rows: `{generationSeed}:{planSlot}`.
3890
+ bank_item_key String?
3725
3891
  question String
3726
3892
  answer String
3727
3893
  options String[] // For MCQ's
@@ -3738,6 +3904,7 @@ model Question {
3738
3904
  evaluationPlan EvaluationPlan? @relation(fields: [evaluation_plan_id], references: [id], onDelete: SetNull)
3739
3905
  candidateResponse CandidateQuestionResponse[]
3740
3906
 
3907
+ @@unique([assessment_library_id, bank_item_key])
3741
3908
  @@schema("public")
3742
3909
  }
3743
3910
 
@@ -3804,6 +3971,25 @@ model ReportingOverviewJobRecruiter {
3804
3971
  @@schema("analytics")
3805
3972
  }
3806
3973
 
3974
+ /// Lifecycle of an AI-authored resume. Legacy/uploaded rows leave this null.
3975
+ enum ResumeStatus {
3976
+ DRAFT
3977
+ GENERATING
3978
+ COMPLETED
3979
+
3980
+ @@schema("public")
3981
+ }
3982
+
3983
+ /// How a resume came to exist. Legacy rows leave this null (read as UPLOADED).
3984
+ enum ResumeOrigin {
3985
+ UPLOADED
3986
+ PARSED
3987
+ AI_GENERATED
3988
+ TAILORED
3989
+
3990
+ @@schema("public")
3991
+ }
3992
+
3807
3993
  model Resume {
3808
3994
  id String @id @default(uuid())
3809
3995
  user_id String?
@@ -3840,8 +4026,40 @@ model Resume {
3840
4026
  candidate_profile CandidateProfile? // one-to-one link
3841
4027
  yearsOfExperience Int?
3842
4028
 
4029
+ /// Lifecycle for AI-authored resumes. Null for legacy/uploaded rows (treated as COMPLETED).
4030
+ status ResumeStatus?
4031
+ /// How this resume came to exist. Null for legacy rows (treated as UPLOADED).
4032
+ origin ResumeOrigin?
4033
+ /// Intake session id echoed to the client so a refresh can resume the conversation.
4034
+ intake_session_id String?
4035
+ /// Durable copy of the chat transcript for the AI intake; Redis is the hot path.
4036
+ conversation_history Json?
4037
+ /// Progress 0-100 reported by the ResumeIntake workflow.
4038
+ intake_progress Int?
4039
+ /// Last Orion execution id for the intake/generate workflow, for tracing.
4040
+ orion_execution_id String?
4041
+ /// Snapshot of the candidate's own words, taken immediately before the
4042
+ /// `GenerateResumeFromIntake` polish overwrites `resume_data`. Makes the
4043
+ /// polish reversible, keeps regeneration idempotent (always polish from raw,
4044
+ /// never from already-polished prose), and is the base for a "what changed"
4045
+ /// diff. Null until the first generate; cleared again on revert.
4046
+ intake_raw_data Json?
4047
+
4048
+ /// Raw job description text the candidate pasted to start a tailoring session.
4049
+ tailoring_jd_text String?
4050
+ /// Cached JobDescriptionParser output; computed once per tailoring session.
4051
+ tailoring_jd_parsed Json?
4052
+ /// Cached ResumeGapAnalysis output; computed once per tailoring session.
4053
+ tailoring_gap_analysis Json?
4054
+ /// Working tailoring state: the draft ResumeFormData, tailoring notes, the
4055
+ /// pending contradiction awaiting resolution, and the question counter.
4056
+ /// Scratch space only — the draft is materialised into `resume_data` when the
4057
+ /// candidate saves it.
4058
+ tailoring_draft Json?
4059
+
3843
4060
  @@index([user_id])
3844
4061
  @@index([file_hash])
4062
+ @@index([user_id, status])
3845
4063
  @@schema("public")
3846
4064
  }
3847
4065
 
@@ -4336,6 +4554,8 @@ model User {
4336
4554
  created_by String
4337
4555
  updated_by String
4338
4556
  is_active Boolean @default(true)
4557
+ /// Set when removed from team (Delete); null for active/inactive-only users.
4558
+ deleted_at DateTime?
4339
4559
  is_self_registered Boolean @default(true)
4340
4560
  permission_id String? @unique
4341
4561
  preferences Json? @default("{}")
@@ -4381,32 +4601,37 @@ model User {
4381
4601
  notes Notes[]
4382
4602
  recruiter_applications Application[] @relation("ApplicationRecruiter")
4383
4603
 
4384
- name String?
4385
- phone String?
4604
+ // user proctoring
4605
+ proctoringSessions ProctoringSession[]
4606
+
4607
+ name String?
4608
+ phone String?
4386
4609
  /// Contact email for interviews, assessments, and resume export. Login stays on `email`.
4387
- communication_email String?
4388
- description String?
4389
- date_of_birth DateTime?
4390
- designation String?
4391
- location String?
4392
- salary String?
4393
- work_experience Json?
4394
- education Json?
4395
- skills Json?
4396
- notice_period String?
4397
- preferred_work_mode String?
4398
- gender String?
4399
- ethnicity String?
4400
- veteran_status String?
4401
- disability_status String?
4402
- professional_expertise String?
4403
- documents Document[]
4404
- offers Offer[] @relation("OfferCandidate")
4610
+ communication_email String?
4611
+ description String?
4612
+ date_of_birth DateTime?
4613
+ designation String?
4614
+ location String?
4615
+ salary String?
4616
+ work_experience Json?
4617
+ education Json?
4618
+ skills Json?
4619
+ notice_period String?
4620
+ preferred_work_mode String?
4621
+ gender String?
4622
+ ethnicity String?
4623
+ veteran_status String?
4624
+ self_registered_organisation_name String?
4625
+ disability_status String?
4626
+ professional_expertise String?
4627
+ documents Document[]
4628
+ offers Offer[] @relation("OfferCandidate")
4405
4629
 
4406
4630
  employee_number Int?
4407
4631
  employee_code String? @unique
4408
4632
 
4409
- department_members DepartmentMember[] @relation("DepartmentMemberships")
4633
+ department_members DepartmentMember[] @relation("DepartmentMemberships")
4634
+ assigned_hiring_units PurchasedHiringUnit[] @relation("AssignedHiringUnits")
4410
4635
 
4411
4636
  @@index([role_id])
4412
4637
  @@index([permission_id])