@adaptware/eagles-db 0.5.57 → 0.5.60

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
@@ -1567,37 +1556,54 @@ enum FitCheckStatus {
1567
1556
  @@schema("public")
1568
1557
  }
1569
1558
 
1559
+ /// 5-level current evaluation. LEVEL_1 is weakest, LEVEL_5 is strongest.
1560
+ /// Display copy and numeric rank live in backend `CurrentEvaluationMapper`.
1561
+ enum CurrentEvaluation {
1562
+ LEVEL_1
1563
+ LEVEL_2
1564
+ LEVEL_3
1565
+ LEVEL_4
1566
+ LEVEL_5
1567
+
1568
+ @@schema("public")
1569
+ }
1570
+
1570
1571
  model FitCheck {
1571
1572
  /// Primary key (UUID)
1572
- id String @id @default(uuid())
1573
+ id String @id @default(uuid())
1573
1574
  /// FK to organisations table
1574
1575
  organisation_id String?
1575
1576
  candidate_id String?
1576
1577
  resume_id String
1577
1578
  job_description_id String
1578
1579
  /// Owning application (inverted from Application.fit_check_id)
1579
- application_id String? @unique
1580
+ application_id String? @unique
1580
1581
  skills_analysis Json?
1581
1582
  overall_analysis Json?
1582
1583
  overall_score Float
1583
1584
  /// Free-text from Overall Analysis agent; preferred over recommendation Json.
1584
1585
  overall_recommendation String?
1586
+ /// Why `overall_score` is this value. Distinct from `overall_recommendation`.
1587
+ score_reasoning String?
1588
+ /// 5-level current evaluation (`CurrentEvaluation`). Independent of overall_score.
1589
+ current_evaluation CurrentEvaluation?
1585
1590
  short_match_analysis Json[]
1586
1591
  fit_comparison Json?
1587
1592
  recommendation Json?
1588
1593
  competencies_analysis Json?
1594
+ competencies_verdict Json?
1589
1595
  /// Audit fields
1590
1596
  created_by String
1591
1597
  updated_by String
1592
- is_active Boolean @default(true)
1593
- created_at DateTime @default(now())
1594
- updated_at DateTime @updatedAt
1598
+ is_active Boolean @default(true)
1599
+ created_at DateTime @default(now())
1600
+ updated_at DateTime @updatedAt
1595
1601
  /// Relations
1596
- organisation Organisation? @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1597
- candidate User? @relation(fields: [candidate_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1598
- resume Resume @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1599
- job_description JobDescription @relation(fields: [job_description_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1600
- application Application? @relation(fields: [application_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1602
+ organisation Organisation? @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1603
+ candidate User? @relation(fields: [candidate_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1604
+ resume Resume @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1605
+ job_description JobDescription @relation(fields: [job_description_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1606
+ application Application? @relation(fields: [application_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
1601
1607
 
1602
1608
  /// Ensure one FitCheck per (resume_id, job_description_id) pair
1603
1609
  @@unique([resume_id, job_description_id])
@@ -1970,18 +1976,21 @@ enum AiAssistanceMode {
1970
1976
  model Interview {
1971
1977
  id String @id @default(uuid())
1972
1978
  /// Foreign keys
1973
- organisation_id String
1979
+ organisation_id String?
1974
1980
  candidate_id String
1975
1981
  interviewer_id String?
1976
- evaluation_plan_id String
1982
+ evaluation_plan_id String?
1977
1983
  application_id String?
1978
1984
  resume_id String
1979
- job_description_id String
1985
+ job_description_id String?
1980
1986
  calendar_event_id String?
1987
+ calendar_events Json?
1988
+ /// True for candidate-initiated profile enrichment voice sessions (not hiring).
1989
+ candidate_initiated Boolean @default(false)
1990
+ /// Profile AI: purpose, focusAreas, focusAreaCoverage, etc.
1991
+ candidate_interview_metadata Json?
1981
1992
  /// Data
1982
1993
  room_name String?
1983
- /// @deprecated Prefer `ai_feedback.feedback` (narrative string on AI feedback JSON).
1984
- interview_summary String[]
1985
1994
  scheduled_start_time DateTime?
1986
1995
  scheduled_end_time DateTime?
1987
1996
  actual_start_time DateTime?
@@ -1995,17 +2004,18 @@ model Interview {
1995
2004
  ai_assistance_mode AiAssistanceMode? @default(ASSISTED)
1996
2005
  interview_status InterviewStatus @default(SCHEDULED)
1997
2006
  interview_platform InterviewPlatform @default(TREADSPACE)
1998
- overall_feedback String?
1999
2007
  /// Feedback JSON fields
2000
2008
  /// AI overall payload: `{ score, rating, feedback? }` where `feedback` is the
2001
- /// narrative summary string (replaces deprecated `interview_summary`).
2009
+ /// narrative summary string.
2002
2010
  ai_feedback Json?
2003
2011
  human_feedback Json?
2004
- section_feedback Json[]
2005
2012
  interview_preparation Json?
2006
2013
  final_feedback Json?
2007
2014
  interview_analysis_ranges Json?
2008
2015
  interview_flow_summary String?
2016
+ /// Extra Orion prose for downstream AI input (FitCheck, interview prep).
2017
+ /// Not interviewer-facing; omit from evidence GET / snapshots / Zoho.
2018
+ ai_context String?
2009
2019
  /// When Orion AI feedback was last persisted on this interview.
2010
2020
  ai_feedback_generated_at DateTime?
2011
2021
  /// Experimental Beta AI feedback store (parallel to production AI columns).
@@ -2014,6 +2024,7 @@ model Interview {
2014
2024
  beta_section_feedback Json[] @default([])
2015
2025
  beta_final_feedback Json?
2016
2026
  beta_interview_flow_summary String?
2027
+ beta_ai_context String?
2017
2028
  beta_ai_feedback_generated_at DateTime?
2018
2029
  /// When recruiter human feedback was last saved (draft or final).
2019
2030
  human_feedback_updated_at DateTime?
@@ -2038,27 +2049,27 @@ model Interview {
2038
2049
  // Relations
2039
2050
  candidate User? @relation("CandidateInterviews", fields: [candidate_id], references: [id], onDelete: SetNull)
2040
2051
  interviewer User? @relation("InterviewerInterviews", fields: [interviewer_id], references: [id], onDelete: SetNull)
2041
- organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2052
+ organisation Organisation? @relation(fields: [organisation_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2042
2053
  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)
2054
+ job_description JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2044
2055
  resume Resume @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2045
2056
  evaluation_plan EvaluationPlan? @relation(fields: [evaluation_plan_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2046
2057
  application Application? @relation(fields: [application_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2047
2058
  ongoing_evaluation OngoingEvaluation?
2048
2059
  transcripts Transcript[]
2049
2060
  suggestions Suggestion[]
2050
- feedback Feedback[]
2051
2061
  interviewNotes InterviewNotes[]
2052
2062
  liveAnalysis LiveAnalysis[]
2053
2063
  zoom_meeting ZoomMeeting?
2054
2064
 
2055
2065
  // Indexes
2056
- @@unique([interviewer_id, candidate_id, evaluation_plan_id])
2057
2066
  @@index([candidate_id])
2067
+ @@index([candidate_id, candidate_initiated, created_at])
2058
2068
  @@index([interviewer_id])
2059
2069
  @@index([job_description_id])
2060
2070
  @@index([organisation_id, interview_status])
2061
2071
  @@index([interview_status, scheduled_start_time])
2072
+ @@index([candidate_initiated, interview_status])
2062
2073
  @@schema("public")
2063
2074
  }
2064
2075
 
@@ -2381,13 +2392,15 @@ model JobApplicationField {
2381
2392
  updated_by String
2382
2393
  created_at DateTime @default(now())
2383
2394
  updated_at DateTime @updatedAt
2395
+ /// order of question
2396
+ order_no Int? //nullable for now to allow migration
2384
2397
  /// Relations
2385
2398
  organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2386
2399
  job_application_template JobApplicationTemplate @relation(fields: [template_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2387
2400
  job_application_responses JobApplicationResponse[]
2388
2401
 
2389
2402
  /// Indexes
2390
- @@index([organisation_id, template_id])
2403
+ @@index([organisation_id, template_id, order_no])
2391
2404
  @@schema("public")
2392
2405
  }
2393
2406
 
@@ -2415,13 +2428,14 @@ model JobApplicationResponse {
2415
2428
  created_by String
2416
2429
  updated_by String
2417
2430
  is_active Boolean @default(true)
2431
+ order_no Int? //nullable for now to allow migration
2418
2432
  /// Relations
2419
2433
  application Application @relation(fields: [application_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
2420
2434
  field JobApplicationField? @relation(fields: [job_application_field_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
2421
2435
  document Document? @relation(fields: [document_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
2422
2436
 
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])
2437
+ // / Partial unique on (application_id, job_application_field_id) WHERE job_application_field_id IS NOT NULL — see migration SQL
2438
+ @@index([application_id, is_active, order_no])
2425
2439
  @@index([job_application_field_id])
2426
2440
  @@schema("public")
2427
2441
  }
@@ -2761,13 +2775,15 @@ model AiCostLedgerEntry {
2761
2775
  @@schema("licensing")
2762
2776
  }
2763
2777
 
2764
- /// Immutable assignment history for Hiring Unit department/job changes.
2778
+ /// Immutable assignment history for Hiring Unit department/user/job changes.
2765
2779
  model PurchasedHiringUnitAssignmentEvent {
2766
2780
  id String @id @default(uuid())
2767
2781
  purchased_hiring_unit_id String
2768
2782
  event_kind PurchasedHiringUnitAssignmentEventKind
2769
2783
  previous_department_id String?
2770
2784
  new_department_id String?
2785
+ previous_assigned_user_id String?
2786
+ new_assigned_user_id String?
2771
2787
  previous_job_description_id String?
2772
2788
  new_job_description_id String?
2773
2789
  effective_at DateTime @default(now())
@@ -2880,6 +2896,8 @@ enum PurchasedTopUpStatus {
2880
2896
  enum PurchasedHiringUnitAssignmentEventKind {
2881
2897
  DEPARTMENT_ASSIGNED
2882
2898
  DEPARTMENT_UNASSIGNED
2899
+ USER_ASSIGNED
2900
+ USER_UNASSIGNED
2883
2901
  JOB_LINKED
2884
2902
  JOB_UNLINKED
2885
2903
 
@@ -3001,6 +3019,8 @@ model PurchasedHiringUnit {
3001
3019
  status PurchasedHiringUnitStatus @default(AVAILABLE)
3002
3020
  /// Current department allocation (licensing allocation, not hiring manager).
3003
3021
  department_id String?
3022
+ /// Optional person reservation. Units with this set are consumed before dept/org pools.
3023
+ assigned_user_id String?
3004
3024
  /// Set once when department_id is first assigned; not cleared on reassignment.
3005
3025
  first_assigned_at DateTime?
3006
3026
  /// Optional unit expiry.
@@ -3017,6 +3037,7 @@ model PurchasedHiringUnit {
3017
3037
  organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
3018
3038
  purchased_license_package PurchasedLicensePackage @relation(fields: [purchased_license_package_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
3019
3039
  department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3040
+ assigned_user User? @relation("AssignedHiringUnits", fields: [assigned_user_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3020
3041
  job_description JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3021
3042
  assignment_events PurchasedHiringUnitAssignmentEvent[]
3022
3043
  ledger_entries LicensingLedgerEntry[]
@@ -3025,9 +3046,11 @@ model PurchasedHiringUnit {
3025
3046
  @@index([organisation_id])
3026
3047
  @@index([purchased_license_package_id])
3027
3048
  @@index([department_id])
3049
+ @@index([assigned_user_id])
3028
3050
  @@index([job_description_id])
3029
3051
  @@index([organisation_id, status])
3030
3052
  @@index([organisation_id, department_id])
3053
+ @@index([organisation_id, assigned_user_id])
3031
3054
  @@index([organisation_id, job_description_id])
3032
3055
  @@map("purchased_hiring_units")
3033
3056
  @@schema("licensing")
@@ -3331,58 +3354,61 @@ enum OngoingEvaluationRoundStatus {
3331
3354
 
3332
3355
  model OngoingEvaluation {
3333
3356
  // Primary Key
3334
- id String @id @default(uuid())
3357
+ id String @id @default(uuid())
3335
3358
  // 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
3359
+ organisation_id String
3360
+ application_id String
3361
+ candidate_id String
3362
+ resume_id String
3363
+ job_description_id String
3364
+ evaluation_plan_id String
3365
+ interview_id String? @unique
3366
+ assignment_id String? @unique
3367
+ assessment_id String? @unique
3345
3368
  // 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?
3369
+ state OngoingEvaluationState? @default(NOT_STARTED)
3370
+ round_status OngoingEvaluationRoundStatus? @default(NOT_SCHEDULED)
3371
+ comments String? @default("")
3372
+ completed_at DateTime?
3373
+ assigned_at DateTime?
3374
+ result_at DateTime?
3375
+ is_committed Boolean?
3353
3376
  /// 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?
3377
+ title String?
3378
+ description String?
3379
+ order_no Int?
3380
+ round_purpose String?
3381
+ focus_areas String[]
3382
+ topics String[]
3383
+ assignee String?
3384
+ elimination_round Boolean? @default(false)
3385
+ evaluation_type EvaluationType?
3386
+ type_of_round RoundType?
3387
+ duration Int?
3388
+ deadline Int?
3389
+ duration_unit DurationUnit?
3390
+ details Json?
3391
+ category EvaluationCategory?
3392
+ /// Round relevance vs JD / application. Unused until a later writer.
3393
+ relevance_score Float?
3394
+ relevance_reasoning String?
3369
3395
  // 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)
3396
+ created_at DateTime @default(now())
3397
+ updated_at DateTime @updatedAt
3398
+ created_by String
3399
+ updated_by String
3400
+ is_active Boolean @default(true)
3401
+ is_current Boolean @default(false)
3376
3402
  // 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)
3403
+ jobDescription JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull)
3404
+ application Application @relation(fields: [application_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3405
+ candidate User? @relation(fields: [candidate_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3406
+ resume Resume? @relation(fields: [resume_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3407
+ organisation Organisation? @relation(fields: [organisation_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3408
+ evaluation_plan EvaluationPlan @relation(fields: [evaluation_plan_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
3409
+ interview Interview? @relation(fields: [interview_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
3410
+ assignment Assignment?
3411
+ assessment Assessment? @relation(fields: [assessment_id], references: [id], onDelete: SetNull)
3386
3412
 
3387
3413
  @@unique([application_id, evaluation_plan_id])
3388
3414
  @@index([application_id])
@@ -3494,6 +3520,15 @@ enum OrganisationSize {
3494
3520
  @@schema("public")
3495
3521
  }
3496
3522
 
3523
+ /// Who may consume unused hiring units. Changing this does not rewrite unit rows.
3524
+ enum HiringUnitAssignmentScope {
3525
+ USER
3526
+ DEPARTMENT
3527
+ ORGANISATION
3528
+
3529
+ @@schema("public")
3530
+ }
3531
+
3497
3532
  model Organisation {
3498
3533
  id String @id @default(uuid())
3499
3534
  name String @unique
@@ -3536,6 +3571,8 @@ model Organisation {
3536
3571
  timezone String @default("Asia/Kolkata")
3537
3572
  /// ISO 3166-1 alpha-2 (e.g. IN).
3538
3573
  country_code String @default("IN")
3574
+ /// Who may consume unused hiring units. Does not rewrite unit rows when changed.
3575
+ hiring_unit_assignment_scope HiringUnitAssignmentScope @default(DEPARTMENT)
3539
3576
  organisation_configs OrganisationConfig[]
3540
3577
  activity_events ActivityEvent[]
3541
3578
  usage_ledger_entries UsageLedgerEntry[]
@@ -3716,12 +3753,24 @@ enum Difficulty {
3716
3753
  @@schema("public")
3717
3754
  }
3718
3755
 
3756
+ enum QuestionCategory {
3757
+ /// Aptitude, logical reasoning, numerical, verbal — non-domain general tests.
3758
+ GENERAL_APTITUDE
3759
+ /// Role/domain/technical knowledge (skills from JD and competencies).
3760
+ DOMAIN_KNOWLEDGE
3761
+
3762
+ @@schema("public")
3763
+ }
3764
+
3719
3765
  model Question {
3720
3766
  id String @id @default(uuid())
3721
3767
  assessment_library_id String?
3722
3768
  evaluation_plan_id String?
3723
3769
  candidate_id String?
3724
3770
  skill String?
3771
+ category QuestionCategory?
3772
+ /// Stable idempotency key for reusable bank rows: `{generationSeed}:{planSlot}`.
3773
+ bank_item_key String?
3725
3774
  question String
3726
3775
  answer String
3727
3776
  options String[] // For MCQ's
@@ -3738,6 +3787,7 @@ model Question {
3738
3787
  evaluationPlan EvaluationPlan? @relation(fields: [evaluation_plan_id], references: [id], onDelete: SetNull)
3739
3788
  candidateResponse CandidateQuestionResponse[]
3740
3789
 
3790
+ @@unique([assessment_library_id, bank_item_key])
3741
3791
  @@schema("public")
3742
3792
  }
3743
3793
 
@@ -3804,6 +3854,25 @@ model ReportingOverviewJobRecruiter {
3804
3854
  @@schema("analytics")
3805
3855
  }
3806
3856
 
3857
+ /// Lifecycle of an AI-authored resume. Legacy/uploaded rows leave this null.
3858
+ enum ResumeStatus {
3859
+ DRAFT
3860
+ GENERATING
3861
+ COMPLETED
3862
+
3863
+ @@schema("public")
3864
+ }
3865
+
3866
+ /// How a resume came to exist. Legacy rows leave this null (read as UPLOADED).
3867
+ enum ResumeOrigin {
3868
+ UPLOADED
3869
+ PARSED
3870
+ AI_GENERATED
3871
+ TAILORED
3872
+
3873
+ @@schema("public")
3874
+ }
3875
+
3807
3876
  model Resume {
3808
3877
  id String @id @default(uuid())
3809
3878
  user_id String?
@@ -3840,8 +3909,40 @@ model Resume {
3840
3909
  candidate_profile CandidateProfile? // one-to-one link
3841
3910
  yearsOfExperience Int?
3842
3911
 
3912
+ /// Lifecycle for AI-authored resumes. Null for legacy/uploaded rows (treated as COMPLETED).
3913
+ status ResumeStatus?
3914
+ /// How this resume came to exist. Null for legacy rows (treated as UPLOADED).
3915
+ origin ResumeOrigin?
3916
+ /// Intake session id echoed to the client so a refresh can resume the conversation.
3917
+ intake_session_id String?
3918
+ /// Durable copy of the chat transcript for the AI intake; Redis is the hot path.
3919
+ conversation_history Json?
3920
+ /// Progress 0-100 reported by the ResumeIntake workflow.
3921
+ intake_progress Int?
3922
+ /// Last Orion execution id for the intake/generate workflow, for tracing.
3923
+ orion_execution_id String?
3924
+ /// Snapshot of the candidate's own words, taken immediately before the
3925
+ /// `GenerateResumeFromIntake` polish overwrites `resume_data`. Makes the
3926
+ /// polish reversible, keeps regeneration idempotent (always polish from raw,
3927
+ /// never from already-polished prose), and is the base for a "what changed"
3928
+ /// diff. Null until the first generate; cleared again on revert.
3929
+ intake_raw_data Json?
3930
+
3931
+ /// Raw job description text the candidate pasted to start a tailoring session.
3932
+ tailoring_jd_text String?
3933
+ /// Cached JobDescriptionParser output; computed once per tailoring session.
3934
+ tailoring_jd_parsed Json?
3935
+ /// Cached ResumeGapAnalysis output; computed once per tailoring session.
3936
+ tailoring_gap_analysis Json?
3937
+ /// Working tailoring state: the draft ResumeFormData, tailoring notes, the
3938
+ /// pending contradiction awaiting resolution, and the question counter.
3939
+ /// Scratch space only — the draft is materialised into `resume_data` when the
3940
+ /// candidate saves it.
3941
+ tailoring_draft Json?
3942
+
3843
3943
  @@index([user_id])
3844
3944
  @@index([file_hash])
3945
+ @@index([user_id, status])
3845
3946
  @@schema("public")
3846
3947
  }
3847
3948
 
@@ -4336,6 +4437,8 @@ model User {
4336
4437
  created_by String
4337
4438
  updated_by String
4338
4439
  is_active Boolean @default(true)
4440
+ /// Set when removed from team (Delete); null for active/inactive-only users.
4441
+ deleted_at DateTime?
4339
4442
  is_self_registered Boolean @default(true)
4340
4443
  permission_id String? @unique
4341
4444
  preferences Json? @default("{}")
@@ -4381,32 +4484,34 @@ model User {
4381
4484
  notes Notes[]
4382
4485
  recruiter_applications Application[] @relation("ApplicationRecruiter")
4383
4486
 
4384
- name String?
4385
- phone String?
4487
+ name String?
4488
+ phone String?
4386
4489
  /// 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")
4490
+ communication_email String?
4491
+ description String?
4492
+ date_of_birth DateTime?
4493
+ designation String?
4494
+ location String?
4495
+ salary String?
4496
+ work_experience Json?
4497
+ education Json?
4498
+ skills Json?
4499
+ notice_period String?
4500
+ preferred_work_mode String?
4501
+ gender String?
4502
+ ethnicity String?
4503
+ veteran_status String?
4504
+ self_registered_organisation_name String?
4505
+ disability_status String?
4506
+ professional_expertise String?
4507
+ documents Document[]
4508
+ offers Offer[] @relation("OfferCandidate")
4405
4509
 
4406
4510
  employee_number Int?
4407
4511
  employee_code String? @unique
4408
4512
 
4409
- department_members DepartmentMember[] @relation("DepartmentMemberships")
4513
+ department_members DepartmentMember[] @relation("DepartmentMemberships")
4514
+ assigned_hiring_units PurchasedHiringUnit[] @relation("AssignedHiringUnits")
4410
4515
 
4411
4516
  @@index([role_id])
4412
4517
  @@index([permission_id])