@dakkitor/api-contracts 1.8.0 → 1.9.2

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.
Files changed (139) hide show
  1. package/dist/abilities/abilities.contract.d.ts +28 -1
  2. package/dist/abilities/abilities.contract.d.ts.map +1 -1
  3. package/dist/abilities/abilities.contract.js +32 -1
  4. package/dist/abilities/abilities.contract.spec.js +63 -3
  5. package/dist/abilities/admin-dashboard.abilities.test.js +1 -0
  6. package/dist/abilities/automatic-caller.abilities.json +1 -0
  7. package/dist/abilities/first-agent.abilities.json +14 -0
  8. package/dist/abilities/interconnection.abilities.json +17 -1
  9. package/dist/abilities/office-manager.abilities.json +4 -0
  10. package/dist/abilities/second-agent.abilities.json +48 -1
  11. package/dist/abilities/worker-curator.abilities.json +17 -1
  12. package/dist/actives/actives.contract.d.ts +6959 -6547
  13. package/dist/actives/actives.contract.d.ts.map +1 -1
  14. package/dist/agent-client-links/agent-client-links.contract.d.ts +1275 -1173
  15. package/dist/agent-client-links/agent-client-links.contract.d.ts.map +1 -1
  16. package/dist/auth/auth.contract.d.ts +102 -102
  17. package/dist/bookings/backdate-guard.spec.js +24 -32
  18. package/dist/bookings/bookings.contract.d.ts +9963 -9305
  19. package/dist/bookings/bookings.contract.d.ts.map +1 -1
  20. package/dist/bookings/bookings.contract.js +2 -16
  21. package/dist/call-history/call-history.contract.d.ts +1040 -828
  22. package/dist/call-history/call-history.contract.d.ts.map +1 -1
  23. package/dist/calls-feed/calls-feed.contract.d.ts +138 -98
  24. package/dist/calls-feed/calls-feed.contract.d.ts.map +1 -1
  25. package/dist/candidate-calling/candidate-calling.contract.d.ts +18372 -0
  26. package/dist/candidate-calling/candidate-calling.contract.d.ts.map +1 -0
  27. package/dist/candidate-calling/candidate-calling.contract.js +494 -0
  28. package/dist/candidate-calling/candidate-calling.contract.spec.d.ts +2 -0
  29. package/dist/candidate-calling/candidate-calling.contract.spec.d.ts.map +1 -0
  30. package/dist/candidate-calling/candidate-calling.contract.spec.js +357 -0
  31. package/dist/candidate-calling/candidate-dialpad-call.schemas.d.ts +10 -0
  32. package/dist/candidate-calling/candidate-dialpad-call.schemas.d.ts.map +1 -0
  33. package/dist/candidate-calling/candidate-dialpad-call.schemas.js +33 -0
  34. package/dist/client-contacts/client-contacts.contract.d.ts +622 -622
  35. package/dist/clients/clients.contract.d.ts +289 -289
  36. package/dist/collaboration-checkings/collaboration-checkings.contract.d.ts +3108 -3004
  37. package/dist/collaboration-checkings/collaboration-checkings.contract.d.ts.map +1 -1
  38. package/dist/collaborations/collaborations.contract.d.ts +2604 -2507
  39. package/dist/collaborations/collaborations.contract.d.ts.map +1 -1
  40. package/dist/common/callback-slots.d.ts +4 -0
  41. package/dist/common/callback-slots.d.ts.map +1 -0
  42. package/dist/common/callback-slots.js +20 -0
  43. package/dist/common/common-schemas.d.ts +10 -4
  44. package/dist/common/common-schemas.d.ts.map +1 -1
  45. package/dist/common/common-schemas.js +6 -0
  46. package/dist/companies/companies.contract.d.ts +120 -120
  47. package/dist/crews/crews.contract.d.ts +278 -278
  48. package/dist/cron-executions/cron-executions.contract.d.ts +245 -245
  49. package/dist/cron-executions/cron-executions.contract.d.ts.map +1 -1
  50. package/dist/cron-executions/cron-executions.contract.js +1 -0
  51. package/dist/curated-worker-leads/candidate-coordination.contract.d.ts +3830 -2354
  52. package/dist/curated-worker-leads/candidate-coordination.contract.d.ts.map +1 -1
  53. package/dist/curated-worker-leads/candidate-coordination.contract.js +73 -7
  54. package/dist/curated-worker-leads/candidate-edit-session.contract.d.ts +94 -94
  55. package/dist/curated-worker-leads/contact-history.spec.js +141 -0
  56. package/dist/curated-worker-leads/job-candidate-cards.spec.js +19 -0
  57. package/dist/curated-worker-leads/job-candidates.spec.js +179 -0
  58. package/dist/curated-worker-leads/jobs-tab.contract.d.ts +8185 -2106
  59. package/dist/curated-worker-leads/jobs-tab.contract.d.ts.map +1 -1
  60. package/dist/curated-worker-leads/jobs-tab.contract.js +126 -1
  61. package/dist/curated-worker-leads/owned-jobs-tab.contract.d.ts +110 -110
  62. package/dist/curated-workers/curated-workers.contract.d.ts +2033 -1951
  63. package/dist/curated-workers/curated-workers.contract.d.ts.map +1 -1
  64. package/dist/dashboards/dashboard-widgets.contact-history.spec.d.ts +2 -0
  65. package/dist/dashboards/dashboard-widgets.contact-history.spec.d.ts.map +1 -0
  66. package/dist/dashboards/dashboard-widgets.contact-history.spec.js +160 -0
  67. package/dist/dashboards/dashboard-widgets.contract.d.ts +1027 -284
  68. package/dist/dashboards/dashboard-widgets.contract.d.ts.map +1 -1
  69. package/dist/dashboards/dashboard-widgets.contract.js +46 -1
  70. package/dist/dashboards/dashboard.contract.d.ts +8 -8
  71. package/dist/document-intake/document-intake.contract.d.ts +1145 -1145
  72. package/dist/export-jobs/export-jobs.contract.d.ts +755 -755
  73. package/dist/exports-events/exports-events.schemas.d.ts +6 -6
  74. package/dist/index.d.ts +5 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +5 -0
  77. package/dist/jobs/jobs.contract.d.ts +8507 -6044
  78. package/dist/jobs/jobs.contract.d.ts.map +1 -1
  79. package/dist/jobs/jobs.contract.js +95 -2
  80. package/dist/jobs/jobs.contract.spec.js +231 -1
  81. package/dist/kpi-impersonation/kpi-impersonation.contract.d.ts +102 -102
  82. package/dist/lead-assignments/lead-assignments.contract.d.ts +2985 -1139
  83. package/dist/lead-assignments/lead-assignments.contract.d.ts.map +1 -1
  84. package/dist/lead-assignments/lead-assignments.contract.js +102 -2
  85. package/dist/lead-assignments/lead-assignments.contract.spec.d.ts +2 -0
  86. package/dist/lead-assignments/lead-assignments.contract.spec.d.ts.map +1 -0
  87. package/dist/lead-assignments/lead-assignments.contract.spec.js +51 -0
  88. package/dist/lead-distribution/agent-lead-distribution.contract.d.ts +341 -317
  89. package/dist/lead-distribution/agent-lead-distribution.contract.d.ts.map +1 -1
  90. package/dist/leads/leads.contract.d.ts +282 -282
  91. package/dist/locations/location-enrichment.contract.spec.d.ts +2 -0
  92. package/dist/locations/location-enrichment.contract.spec.d.ts.map +1 -0
  93. package/dist/locations/location-enrichment.contract.spec.js +50 -0
  94. package/dist/locations/locations.contract.d.ts +132 -98
  95. package/dist/locations/locations.contract.d.ts.map +1 -1
  96. package/dist/locations/locations.contract.js +10 -0
  97. package/dist/notifications/notifications.contract.d.ts +579 -107
  98. package/dist/notifications/notifications.contract.d.ts.map +1 -1
  99. package/dist/notifications/notifications.contract.js +36 -2
  100. package/dist/notifications/notifications.contract.spec.d.ts +2 -0
  101. package/dist/notifications/notifications.contract.spec.d.ts.map +1 -0
  102. package/dist/notifications/notifications.contract.spec.js +39 -0
  103. package/dist/outreach/__tests__/outreach-claim.test.js +33 -0
  104. package/dist/outreach/__tests__/outreach-routes.test.js +8 -0
  105. package/dist/outreach/outreach.contract.d.ts +583 -162
  106. package/dist/outreach/outreach.contract.d.ts.map +1 -1
  107. package/dist/outreach/outreach.contract.js +50 -1
  108. package/dist/outreach/relay-hub-outreach.contract.d.ts +299 -121
  109. package/dist/outreach/relay-hub-outreach.contract.d.ts.map +1 -1
  110. package/dist/outreach/relay-hub-outreach.contract.js +10 -0
  111. package/dist/outreach/relay-hub-outreach.contract.spec.js +26 -0
  112. package/dist/outreach/relay-hub-outreach.schemas.d.ts +94 -0
  113. package/dist/outreach/relay-hub-outreach.schemas.d.ts.map +1 -0
  114. package/dist/outreach/relay-hub-outreach.schemas.js +15 -0
  115. package/dist/own-research/check-phone-response.spec.d.ts +2 -0
  116. package/dist/own-research/check-phone-response.spec.d.ts.map +1 -0
  117. package/dist/own-research/check-phone-response.spec.js +54 -0
  118. package/dist/own-research/own-research.contract.d.ts +971 -820
  119. package/dist/own-research/own-research.contract.d.ts.map +1 -1
  120. package/dist/own-research/own-research.contract.js +6 -1
  121. package/dist/proofs/proofs.contract.d.ts +356 -356
  122. package/dist/proofs/proofs.dtos.d.ts +148 -148
  123. package/dist/realtime/job-call-priority-updated.d.ts +18 -0
  124. package/dist/realtime/job-call-priority-updated.d.ts.map +1 -0
  125. package/dist/realtime/job-call-priority-updated.js +17 -0
  126. package/dist/shared/summary-schemas.d.ts +36 -27
  127. package/dist/shared/summary-schemas.d.ts.map +1 -1
  128. package/dist/shared/summary-schemas.js +7 -0
  129. package/dist/sheets-sync/sheets-sync.contract.d.ts +683 -683
  130. package/dist/user-access-grants/user-access-grants.contract.d.ts +216 -216
  131. package/dist/users/users.contract.d.ts +365 -336
  132. package/dist/users/users.contract.d.ts.map +1 -1
  133. package/dist/users/users.contract.js +9 -1
  134. package/dist/users/users.contract.spec.js +39 -0
  135. package/dist/worker-source-identity-conflicts/worker-source-identity-conflicts.contract.d.ts +150 -150
  136. package/dist/workers/workers.contract.d.ts +4505 -4261
  137. package/dist/workers/workers.contract.d.ts.map +1 -1
  138. package/dist/workers/workers.contract.js +12 -0
  139. package/package.json +1 -1
@@ -5,6 +5,13 @@ const ID = '7d5c0f4e-8f60-4bff-b544-000000000001';
5
5
  const JOB_ID = '7d5c0f4e-8f60-4bff-b544-000000000002';
6
6
  const SUBJECT_ID = '7d5c0f4e-8f60-4bff-b544-000000000003';
7
7
  describe('candidate contact history contract', () => {
8
+ it('rejects an impossible NOT_LOOKING return date', () => {
9
+ expect(candidate_coordination_contract_1.ContactWorkerStateSchema.safeParse({
10
+ kind: 'NOT_LOOKING',
11
+ days: 30,
12
+ returnDate: '2026-02-31',
13
+ }).success).toBe(false);
14
+ });
8
15
  it('parses structured worker and job filters', () => {
9
16
  expect(candidate_coordination_contract_1.CandidateContactHistoryQuerySchema.parse({
10
17
  contactedById: ID,
@@ -62,6 +69,8 @@ describe('candidate contact history contract', () => {
62
69
  },
63
70
  },
64
71
  channel: 'WHATSAPP',
72
+ direction: 'OUTBOUND',
73
+ durationSeconds: null,
65
74
  contactedAt: '2026-07-17T09:00:00.000Z',
66
75
  outcome: 'AWAITING_FEEDBACK',
67
76
  feedbackAt: null,
@@ -85,5 +94,137 @@ describe('candidate contact history contract', () => {
85
94
  expect(candidate_coordination_contract_1.candidateCoordinationContract.contactHistory.method).toBe('GET');
86
95
  expect(candidate_coordination_contract_1.candidateCoordinationContract.recordWhatsAppFeedback.method).toBe('POST');
87
96
  expect(candidate_coordination_contract_1.candidateCoordinationContract.recordContactFeedback.path).toBe('/v2/candidate-contacts/:contactEventId/feedback');
97
+ expect(candidate_coordination_contract_1.CompletePhoneCallBodySchema.safeParse({
98
+ outcome: 'FAILED',
99
+ scope: 'THIS_JOB',
100
+ reason: 'DISTANCE',
101
+ }).success).toBe(false);
102
+ expect(candidate_coordination_contract_1.ContactConnectionResultSchema.safeParse('FAILED').success).toBe(false);
103
+ });
104
+ it('parses job-agnostic Dialpad history outcomes with call metadata', () => {
105
+ const baseItem = {
106
+ id: ID,
107
+ agentId: ID,
108
+ subjectKind: 'CURATED',
109
+ subjectId: SUBJECT_ID,
110
+ viewerWorkerId: null,
111
+ worker: {
112
+ firstName: 'Jamie',
113
+ lastName: 'Taylor',
114
+ postcode: null,
115
+ },
116
+ channel: 'PHONE',
117
+ contactedAt: '2026-08-21T09:00:00.000Z',
118
+ feedbackAt: null,
119
+ feedback: null,
120
+ canRecordFeedback: false,
121
+ };
122
+ expect(candidate_coordination_contract_1.CandidateContactHistoryItemSchema.parse({
123
+ ...baseItem,
124
+ job: null,
125
+ direction: 'INBOUND',
126
+ durationSeconds: 42,
127
+ outcome: 'CONNECTED',
128
+ connection: 'CONNECTED',
129
+ })).toMatchObject({ job: null, direction: 'INBOUND', outcome: 'CONNECTED' });
130
+ expect(candidate_coordination_contract_1.CandidateContactHistoryItemSchema.parse({
131
+ ...baseItem,
132
+ job: null,
133
+ direction: 'OUTBOUND',
134
+ durationSeconds: null,
135
+ outcome: 'FAILED',
136
+ connection: 'FAILED',
137
+ })).toMatchObject({ direction: 'OUTBOUND', outcome: 'FAILED' });
138
+ });
139
+ it.each(['PROCESSING', 'NEEDS_REVIEW', 'FEEDBACK_RECORDED'])('projects managed %s status without private artifacts', (managedFeedbackStatus) => {
140
+ const response = {
141
+ items: [
142
+ {
143
+ id: ID,
144
+ agentId: ID,
145
+ subjectKind: 'CURATED',
146
+ subjectId: SUBJECT_ID,
147
+ viewerWorkerId: null,
148
+ worker: {
149
+ firstName: 'Jamie',
150
+ lastName: 'Taylor',
151
+ postcode: null,
152
+ },
153
+ job: {
154
+ id: JOB_ID,
155
+ tradeName: null,
156
+ postcode: null,
157
+ firstAgent: null,
158
+ },
159
+ channel: 'PHONE',
160
+ direction: 'OUTBOUND',
161
+ durationSeconds: null,
162
+ contactedAt: '2026-08-19T12:00:00.000Z',
163
+ outcome: 'AWAITING_FEEDBACK',
164
+ feedbackAt: null,
165
+ feedback: null,
166
+ managedCallId: SUBJECT_ID,
167
+ managedFeedbackStatus,
168
+ canRecordFeedback: managedFeedbackStatus === 'NEEDS_REVIEW',
169
+ },
170
+ ],
171
+ total: 1,
172
+ hasMore: false,
173
+ totalPages: 1,
174
+ };
175
+ const parsed = candidate_coordination_contract_1.CandidateContactHistoryResponseSchema.parse(response);
176
+ expect(parsed.items[0]).toMatchObject({
177
+ managedCallId: SUBJECT_ID,
178
+ managedFeedbackStatus,
179
+ });
180
+ for (const privateExtra of [
181
+ { recordingUrl: 'https://private.example.test/recording' },
182
+ { transcript: 'private transcript' },
183
+ { analysis: { raw: true } },
184
+ { unknown: true },
185
+ ]) {
186
+ expect(candidate_coordination_contract_1.CandidateContactHistoryResponseSchema.safeParse({
187
+ ...response,
188
+ items: [{ ...response.items[0], ...privateExtra }],
189
+ }).success).toBe(false);
190
+ }
191
+ expect(candidate_coordination_contract_1.CandidateContactHistoryResponseSchema.safeParse({
192
+ ...response,
193
+ recordingUrl: 'https://private.example.test/recording',
194
+ }).success).toBe(false);
195
+ });
196
+ it('projects managed call presentation fields without relaxing item strictness', () => {
197
+ const item = {
198
+ id: ID,
199
+ agentId: ID,
200
+ subjectKind: 'CURATED',
201
+ subjectId: SUBJECT_ID,
202
+ viewerWorkerId: null,
203
+ worker: {
204
+ firstName: 'Jamie',
205
+ lastName: 'Taylor',
206
+ postcode: null,
207
+ },
208
+ job: {
209
+ id: JOB_ID,
210
+ tradeName: null,
211
+ postcode: null,
212
+ firstAgent: null,
213
+ },
214
+ channel: 'PHONE',
215
+ direction: 'OUTBOUND',
216
+ durationSeconds: null,
217
+ contactedAt: '2026-08-19T12:00:00.000Z',
218
+ outcome: 'AWAITING_FEEDBACK',
219
+ feedbackAt: null,
220
+ feedback: null,
221
+ managedCallStatus: 'NEEDS_REVIEW',
222
+ managedHasRecording: true,
223
+ managedCallSource: 'HISTORICAL_IMPORT',
224
+ canRecordFeedback: true,
225
+ };
226
+ expect(candidate_coordination_contract_1.CandidateContactHistoryItemSchema.safeParse(item).success).toBe(true);
227
+ expect(candidate_coordination_contract_1.CandidateContactHistoryItemSchema.safeParse({ ...item, unknown: true })
228
+ .success).toBe(false);
88
229
  });
89
230
  });
@@ -67,6 +67,25 @@ describe('JobCandidateCardSchema / OwnedCandidateCardSchema', () => {
67
67
  expect(parsed).not.toHaveProperty('email');
68
68
  expect(parsed).not.toHaveProperty('nino');
69
69
  });
70
+ it('keeps list ranking metadata out of the card hydration projection', () => {
71
+ const parsed = jobs_tab_contract_1.JobCandidateCardSchema.parse({
72
+ id: '7d5c0f4e-8f60-4bff-b544-111111111111',
73
+ firstName: 'Ana',
74
+ hasTools: true,
75
+ hasPpe: true,
76
+ offerOpenness: 'OPEN',
77
+ createdAt: new Date().toISOString(),
78
+ updatedAt: new Date().toISOString(),
79
+ qualifications: [],
80
+ hasPhoneNumber: true,
81
+ ranking: {
82
+ score: 84,
83
+ rank: 1,
84
+ reservationOwnerId: '6f8e5c4d-3b2a-4190-8c7d-6e5f4a3b2c1d',
85
+ },
86
+ });
87
+ expect(parsed).not.toHaveProperty('ranking');
88
+ });
70
89
  it('strips sensitive keys nested inside curated candidate data', () => {
71
90
  const parsed = jobs_tab_contract_1.JobCandidateCardSchema.parse({
72
91
  id: '7d5c0f4e-8f60-4bff-b544-111111111111',
@@ -36,6 +36,12 @@ describe('JobCandidates schemas', () => {
36
36
  qualifications: [],
37
37
  hasPhoneNumber: true,
38
38
  };
39
+ it('accepts only UUID candidate snapshot identities', () => {
40
+ const snapshotId = '00000000-0000-4000-8000-000000000001';
41
+ expect(jobs_tab_contract_1.jobsTabContract.candidates.query.parse({ page: 2, snapshotId })).toEqual({ page: 2, snapshotId });
42
+ expect(jobs_tab_contract_1.jobsTabContract.candidates.query.safeParse({ snapshotId: 'shared' })
43
+ .success).toBe(false);
44
+ });
39
45
  it('parses a full row', () => {
40
46
  expect(jobs_tab_contract_1.CandidateRowSchema.parse({
41
47
  ...row,
@@ -56,6 +62,163 @@ describe('JobCandidates schemas', () => {
56
62
  },
57
63
  });
58
64
  });
65
+ it('parses an optional complete call-priority ranking', () => {
66
+ const parsed = jobs_tab_contract_1.CandidateRowSchema.parse({
67
+ ...row,
68
+ ranking: {
69
+ score: 84,
70
+ rank: 1,
71
+ baseFit: 82,
72
+ continuityBoost: 14.3,
73
+ confidence: 'MEDIUM',
74
+ reasons: [
75
+ { code: 'PRACTICAL_COMMUTE', routeMinutes: 14 },
76
+ { code: 'QUALIFICATIONS_PARTIAL', matched: 2, required: 3 },
77
+ ],
78
+ modelVersion: 'candidate-call-priority-v2',
79
+ computedAt: '2026-08-23T09:00:00.000Z',
80
+ decisionEvidence: {
81
+ proximity: {
82
+ score: 0.72,
83
+ routeMinutes: 14,
84
+ maxTravelMinutes: 50,
85
+ distanceKm: 5.2,
86
+ driveMinutes: 22,
87
+ transitMinutes: 48,
88
+ },
89
+ qualification: {
90
+ score: 0.8,
91
+ required: 3,
92
+ matched: 2,
93
+ explicit: true,
94
+ },
95
+ reliability: {
96
+ score: 0.76,
97
+ workerPositiveOutcomes: 6,
98
+ workerMatureOutcomes: 7,
99
+ tradePositiveOutcomes: 15,
100
+ tradeMatureOutcomes: 20,
101
+ globalPrior: 0.6,
102
+ },
103
+ readiness: {
104
+ score: 1,
105
+ state: 'CURRENT_OPEN',
106
+ offerOpenness: 'OPEN',
107
+ offerOpennessSetAt: '2026-08-22T09:00:00.000Z',
108
+ },
109
+ continuity: {
110
+ boost: 14.3,
111
+ outcome: 'CONNECTED',
112
+ acquiredAt: '2026-08-22T08:00:00.000Z',
113
+ expiresAt: '2026-08-24T08:00:00.000Z',
114
+ },
115
+ confidenceFlags: {
116
+ qualificationsExplicit: true,
117
+ matureReliability: true,
118
+ readinessCurrent: true,
119
+ },
120
+ },
121
+ },
122
+ });
123
+ expect(parsed.ranking).toEqual({
124
+ score: 84,
125
+ rank: 1,
126
+ baseFit: 82,
127
+ continuityBoost: 14.3,
128
+ confidence: 'MEDIUM',
129
+ reasons: [
130
+ { code: 'PRACTICAL_COMMUTE', routeMinutes: 14 },
131
+ { code: 'QUALIFICATIONS_PARTIAL', matched: 2, required: 3 },
132
+ ],
133
+ modelVersion: 'candidate-call-priority-v2',
134
+ computedAt: '2026-08-23T09:00:00.000Z',
135
+ decisionEvidence: {
136
+ proximity: {
137
+ score: 0.72,
138
+ routeMinutes: 14,
139
+ maxTravelMinutes: 50,
140
+ distanceKm: 5.2,
141
+ driveMinutes: 22,
142
+ transitMinutes: 48,
143
+ },
144
+ qualification: {
145
+ score: 0.8,
146
+ required: 3,
147
+ matched: 2,
148
+ explicit: true,
149
+ },
150
+ reliability: {
151
+ score: 0.76,
152
+ workerPositiveOutcomes: 6,
153
+ workerMatureOutcomes: 7,
154
+ tradePositiveOutcomes: 15,
155
+ tradeMatureOutcomes: 20,
156
+ globalPrior: 0.6,
157
+ },
158
+ readiness: {
159
+ score: 1,
160
+ state: 'CURRENT_OPEN',
161
+ offerOpenness: 'OPEN',
162
+ offerOpennessSetAt: '2026-08-22T09:00:00.000Z',
163
+ },
164
+ continuity: {
165
+ boost: 14.3,
166
+ outcome: 'CONNECTED',
167
+ acquiredAt: '2026-08-22T08:00:00.000Z',
168
+ expiresAt: '2026-08-24T08:00:00.000Z',
169
+ },
170
+ confidenceFlags: {
171
+ qualificationsExplicit: true,
172
+ matureReliability: true,
173
+ readinessCurrent: true,
174
+ },
175
+ },
176
+ });
177
+ });
178
+ it('rejects half-populated candidate rankings', () => {
179
+ expect(jobs_tab_contract_1.CandidateRowSchema.safeParse({
180
+ ...row,
181
+ ranking: { score: 84, rank: 1 },
182
+ }).success).toBe(false);
183
+ });
184
+ it('rejects candidate rankings outside their score bounds', () => {
185
+ expect(jobs_tab_contract_1.CandidateRowSchema.safeParse({
186
+ ...row,
187
+ ranking: {
188
+ score: 101,
189
+ rank: 1,
190
+ baseFit: 82,
191
+ continuityBoost: 14.3,
192
+ confidence: 'MEDIUM',
193
+ reasons: [{ code: 'PRACTICAL_COMMUTE', routeMinutes: 14 }],
194
+ modelVersion: 'candidate-call-priority-v2',
195
+ computedAt: '2026-08-23T09:00:00.000Z',
196
+ },
197
+ }).success).toBe(false);
198
+ });
199
+ it('rejects unknown ranking confidence and mismatched reason payloads', () => {
200
+ const ranking = {
201
+ score: 84,
202
+ rank: 1,
203
+ baseFit: 82,
204
+ continuityBoost: 14.3,
205
+ confidence: 'MEDIUM',
206
+ reasons: [{ code: 'PRACTICAL_COMMUTE', routeMinutes: 14 }],
207
+ modelVersion: 'candidate-call-priority-v2',
208
+ computedAt: '2026-08-23T09:00:00.000Z',
209
+ };
210
+ expect(jobs_tab_contract_1.CandidateRowSchema.safeParse({
211
+ ...row,
212
+ ranking: { ...ranking, confidence: 'UNKNOWN' },
213
+ }).success).toBe(false);
214
+ expect(jobs_tab_contract_1.CandidateRowSchema.safeParse({
215
+ ...row,
216
+ ranking: {
217
+ ...ranking,
218
+ reasons: [{ code: 'PRACTICAL_COMMUTE', matched: 2, required: 3 }],
219
+ },
220
+ }).success).toBe(false);
221
+ });
59
222
  it('parses a never-contacted row (nullables)', () => {
60
223
  const parsed = jobs_tab_contract_1.CandidateRowSchema.parse({
61
224
  ...row,
@@ -81,8 +244,10 @@ describe('JobCandidates schemas', () => {
81
244
  const parsed = jobs_tab_contract_1.CandidateRowSchema.parse(row);
82
245
  expect(parsed.offerChannels).toBeUndefined();
83
246
  expect(parsed.offerContactedAt).toBeUndefined();
247
+ expect(parsed.ranking).toBeUndefined();
84
248
  });
85
249
  it('parses the response envelope', () => {
250
+ const snapshotId = '00000000-0000-4000-8000-000000000001';
86
251
  const parsed = jobs_tab_contract_1.JobCandidatesResponseSchema.parse({
87
252
  owned: [row],
88
253
  curated: [],
@@ -96,10 +261,24 @@ describe('JobCandidates schemas', () => {
96
261
  workersContactedByViewerCount: 0,
97
262
  workersContactedByOthersCount: 0,
98
263
  dismissed: [],
264
+ orderSnapshot: {
265
+ snapshotId,
266
+ mode: 'RANKED',
267
+ modelVersion: 'candidate-call-priority-v2',
268
+ computedAt: '2026-08-23T09:00:00.000Z',
269
+ rankedCandidateCount: 1,
270
+ },
99
271
  });
100
272
  expect(parsed.totalOwned).toBe(1);
101
273
  expect(parsed.page).toBe(1);
102
274
  expect(parsed.cards.own[0]?.id).toBe(row.subjectId);
275
+ expect(parsed).toMatchObject({
276
+ orderSnapshot: {
277
+ snapshotId,
278
+ mode: 'RANKED',
279
+ rankedCandidateCount: 1,
280
+ },
281
+ });
103
282
  });
104
283
  it('requires the atomic candidate card projection', () => {
105
284
  expect(() => jobs_tab_contract_1.JobCandidatesResponseSchema.parse({