@oxyhq/crowdsource-contracts 0.1.0

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 (57) hide show
  1. package/README.md +129 -0
  2. package/dist/case-envelope.d.ts +1130 -0
  3. package/dist/case-envelope.d.ts.map +1 -0
  4. package/dist/case-envelope.js +383 -0
  5. package/dist/case-envelope.js.map +1 -0
  6. package/dist/decisions.d.ts +353 -0
  7. package/dist/decisions.d.ts.map +1 -0
  8. package/dist/decisions.js +198 -0
  9. package/dist/decisions.js.map +1 -0
  10. package/dist/index.d.ts +45 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +61 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/json-schema.d.ts +43 -0
  15. package/dist/json-schema.d.ts.map +1 -0
  16. package/dist/json-schema.js +83 -0
  17. package/dist/json-schema.js.map +1 -0
  18. package/dist/policies.d.ts +286 -0
  19. package/dist/policies.d.ts.map +1 -0
  20. package/dist/policies.js +178 -0
  21. package/dist/policies.js.map +1 -0
  22. package/dist/primitives.d.ts +185 -0
  23. package/dist/primitives.d.ts.map +1 -0
  24. package/dist/primitives.js +231 -0
  25. package/dist/primitives.js.map +1 -0
  26. package/dist/reputation-events.d.ts +349 -0
  27. package/dist/reputation-events.d.ts.map +1 -0
  28. package/dist/reputation-events.js +128 -0
  29. package/dist/reputation-events.js.map +1 -0
  30. package/dist/resources.d.ts +484 -0
  31. package/dist/resources.d.ts.map +1 -0
  32. package/dist/resources.js +436 -0
  33. package/dist/resources.js.map +1 -0
  34. package/dist/reviews.d.ts +276 -0
  35. package/dist/reviews.d.ts.map +1 -0
  36. package/dist/reviews.js +144 -0
  37. package/dist/reviews.js.map +1 -0
  38. package/dist/taxonomy.d.ts +266 -0
  39. package/dist/taxonomy.d.ts.map +1 -0
  40. package/dist/taxonomy.js +282 -0
  41. package/dist/taxonomy.js.map +1 -0
  42. package/dist/webhooks.d.ts +604 -0
  43. package/dist/webhooks.d.ts.map +1 -0
  44. package/dist/webhooks.js +192 -0
  45. package/dist/webhooks.js.map +1 -0
  46. package/package.json +56 -0
  47. package/src/case-envelope.ts +433 -0
  48. package/src/decisions.ts +216 -0
  49. package/src/index.ts +45 -0
  50. package/src/json-schema.ts +89 -0
  51. package/src/policies.ts +203 -0
  52. package/src/primitives.ts +283 -0
  53. package/src/reputation-events.ts +144 -0
  54. package/src/resources.ts +489 -0
  55. package/src/reviews.ts +159 -0
  56. package/src/taxonomy.ts +313 -0
  57. package/src/webhooks.ts +215 -0
@@ -0,0 +1,276 @@
1
+ /**
2
+ * What one reviewer submits (§9.2, §9.3) and how they step away (§4.1).
3
+ *
4
+ * §9.2 splits the form in two — describe the material, then evaluate it against
5
+ * a rule — to reduce anchoring on the reporter's category and to let the same
6
+ * description be reused under different policies. §9.3 then gives one flat
7
+ * result object. Both are true at once: a `ReviewFinding` carries the step-one
8
+ * fields (`code`, `resourceIds`, `severity`, `confidence`) and the step-two
9
+ * fields (`policyRuleIds`), and the enclosing submission carries the step-two
10
+ * verdict (`outcome`, `recommendedActions`). The split is a property of the
11
+ * interface, not of the payload, so it is documented here rather than nested.
12
+ *
13
+ * The submission is `.strict()`, and that is a safety decision, not tidiness.
14
+ * A review belongs to an assignment the server issued; the assignment id comes
15
+ * from the route, the case and the reviewer come from the assignment. If this
16
+ * object tolerated a `caseId`, an `assignmentId` or a `reviewerId`, the day
17
+ * somebody read one of them would be the day a reviewer could vote on a case
18
+ * they were never drawn for — against "nobody chooses the case they review".
19
+ * Strict makes that field impossible rather than merely unused.
20
+ */
21
+ import { z } from 'zod';
22
+ /**
23
+ * What a single reviewer can conclude.
24
+ *
25
+ * Narrower than §9.6's decision outcomes on purpose. `inconclusive` is what the
26
+ * consensus engine reports when a panel does not agree — a single reviewer
27
+ * cannot fail to agree with themselves, and "the absence of consensus is
28
+ * neither guilt nor innocence" only holds if `inconclusive` is produced by the
29
+ * engine and never voted for. `duplicate`, `escalated` and `superseded` are
30
+ * likewise case-level states, not opinions about material. The plan never
31
+ * enumerates review outcomes; this is that gap resolved toward the invariant.
32
+ */
33
+ export declare const REVIEW_OUTCOMES: readonly ["violation", "no_violation", "insufficient_context", "content_unavailable"];
34
+ export declare const ReviewOutcomeSchema: z.ZodEnum<{
35
+ violation: "violation";
36
+ no_violation: "no_violation";
37
+ insufficient_context: "insufficient_context";
38
+ content_unavailable: "content_unavailable";
39
+ }>;
40
+ export type ReviewOutcome = z.infer<typeof ReviewOutcomeSchema>;
41
+ /** §9.5 feeds this into `contextFactor`: 1.0 if sufficient, 0.5 otherwise. */
42
+ export declare const CONTEXT_SUFFICIENCIES: readonly ["sufficient", "insufficient"];
43
+ export declare const ContextSufficiencySchema: z.ZodEnum<{
44
+ sufficient: "sufficient";
45
+ insufficient: "insufficient";
46
+ }>;
47
+ export type ContextSufficiency = z.infer<typeof ContextSufficiencySchema>;
48
+ /**
49
+ * One structured finding from one reviewer (§9.3).
50
+ *
51
+ * `resourceIds` is required and non-empty: §9.4 makes the affected resource one
52
+ * of the dimensions consensus is measured on, so a finding that does not say
53
+ * what it is about cannot be agreed with or disagreed with.
54
+ *
55
+ * `policyRuleIds` is optional: step one of the form can complete without step
56
+ * two — a reviewer may classify material accurately and find no rule that
57
+ * covers it, which is exactly the `no_violation`-with-findings case §6.2
58
+ * describes.
59
+ *
60
+ * `context` is §9.2's and §9.4's "excepción", written as §6.2 writes it: beside
61
+ * the code and the severity, because "artistic nudity" is a different
62
+ * description of the material from "nudity" rather than a different verdict
63
+ * about it. Optional, and absence means no exception applies.
64
+ *
65
+ * `confidence` communicates quality and triggers escalation (§9.5). It never
66
+ * weights the vote.
67
+ */
68
+ export declare const ReviewFindingSchema: z.ZodObject<{
69
+ code: z.ZodEnum<{
70
+ "integrity.spam": "integrity.spam";
71
+ "integrity.scam": "integrity.scam";
72
+ "integrity.fraud": "integrity.fraud";
73
+ "integrity.impersonation": "integrity.impersonation";
74
+ "integrity.coordinated_manipulation": "integrity.coordinated_manipulation";
75
+ "harassment.insult": "harassment.insult";
76
+ "harassment.targeted_abuse": "harassment.targeted_abuse";
77
+ "harassment.sexual_harassment": "harassment.sexual_harassment";
78
+ "harassment.doxxing": "harassment.doxxing";
79
+ "harassment.credible_threat": "harassment.credible_threat";
80
+ "hate.dehumanization": "hate.dehumanization";
81
+ "hate.slur": "hate.slur";
82
+ "hate.incitement": "hate.incitement";
83
+ "hate.protected_targeting": "hate.protected_targeting";
84
+ "violence.graphic": "violence.graphic";
85
+ "violence.threat": "violence.threat";
86
+ "violence.instruction": "violence.instruction";
87
+ "violence.celebration": "violence.celebration";
88
+ "sexual_content.nudity": "sexual_content.nudity";
89
+ "sexual_content.explicit_activity": "sexual_content.explicit_activity";
90
+ "sexual_content.non_consensual": "sexual_content.non_consensual";
91
+ "sexual_content.exploitation": "sexual_content.exploitation";
92
+ "child_safety.sexualization": "child_safety.sexualization";
93
+ "child_safety.grooming": "child_safety.grooming";
94
+ "child_safety.exploitation": "child_safety.exploitation";
95
+ "self_harm.promotion": "self_harm.promotion";
96
+ "self_harm.instruction": "self_harm.instruction";
97
+ "self_harm.imminent_risk": "self_harm.imminent_risk";
98
+ "privacy.personal_information": "privacy.personal_information";
99
+ "privacy.intimate_media": "privacy.intimate_media";
100
+ "privacy.location_exposure": "privacy.location_exposure";
101
+ "commerce.prohibited_item": "commerce.prohibited_item";
102
+ "commerce.counterfeit": "commerce.counterfeit";
103
+ "commerce.misleading_listing": "commerce.misleading_listing";
104
+ "commerce.unsafe_product": "commerce.unsafe_product";
105
+ "platform_abuse.ban_evasion": "platform_abuse.ban_evasion";
106
+ "platform_abuse.report_abuse": "platform_abuse.report_abuse";
107
+ "platform_abuse.automation_abuse": "platform_abuse.automation_abuse";
108
+ "other.policy_specific": "other.policy_specific";
109
+ "other.unclassifiable": "other.unclassifiable";
110
+ }>;
111
+ resourceIds: z.ZodArray<z.ZodString>;
112
+ severity: z.ZodEnum<{
113
+ low: "low";
114
+ medium: "medium";
115
+ high: "high";
116
+ critical: "critical";
117
+ }>;
118
+ context: z.ZodOptional<z.ZodEnum<{
119
+ artistic: "artistic";
120
+ educational: "educational";
121
+ documentary: "documentary";
122
+ newsworthy: "newsworthy";
123
+ satire: "satire";
124
+ counter_speech: "counter_speech";
125
+ medical: "medical";
126
+ consensual: "consensual";
127
+ fictional: "fictional";
128
+ }>>;
129
+ confidence: z.ZodNumber;
130
+ policyRuleIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
131
+ }, z.core.$strict>;
132
+ export type ReviewFinding = z.infer<typeof ReviewFindingSchema>;
133
+ /**
134
+ * The body of `POST /v1/reviewer/assignments/{id}/reviews` — §9.3's "result of
135
+ * a review".
136
+ *
137
+ * `recommendedActions` is a list of action tokens here, where a decision's
138
+ * `recommendedActions` are objects that name their target resources. That
139
+ * difference is in the plan (§9.3 versus Appendix B) and is kept: a reviewer
140
+ * recommends a course of action, and the consensus process is what binds an
141
+ * agreed recommendation to the specific resources it applies to.
142
+ */
143
+ export declare const ReviewSubmissionSchema: z.ZodObject<{
144
+ outcome: z.ZodEnum<{
145
+ violation: "violation";
146
+ no_violation: "no_violation";
147
+ insufficient_context: "insufficient_context";
148
+ content_unavailable: "content_unavailable";
149
+ }>;
150
+ contextSufficiency: z.ZodEnum<{
151
+ sufficient: "sufficient";
152
+ insufficient: "insufficient";
153
+ }>;
154
+ findings: z.ZodArray<z.ZodObject<{
155
+ code: z.ZodEnum<{
156
+ "integrity.spam": "integrity.spam";
157
+ "integrity.scam": "integrity.scam";
158
+ "integrity.fraud": "integrity.fraud";
159
+ "integrity.impersonation": "integrity.impersonation";
160
+ "integrity.coordinated_manipulation": "integrity.coordinated_manipulation";
161
+ "harassment.insult": "harassment.insult";
162
+ "harassment.targeted_abuse": "harassment.targeted_abuse";
163
+ "harassment.sexual_harassment": "harassment.sexual_harassment";
164
+ "harassment.doxxing": "harassment.doxxing";
165
+ "harassment.credible_threat": "harassment.credible_threat";
166
+ "hate.dehumanization": "hate.dehumanization";
167
+ "hate.slur": "hate.slur";
168
+ "hate.incitement": "hate.incitement";
169
+ "hate.protected_targeting": "hate.protected_targeting";
170
+ "violence.graphic": "violence.graphic";
171
+ "violence.threat": "violence.threat";
172
+ "violence.instruction": "violence.instruction";
173
+ "violence.celebration": "violence.celebration";
174
+ "sexual_content.nudity": "sexual_content.nudity";
175
+ "sexual_content.explicit_activity": "sexual_content.explicit_activity";
176
+ "sexual_content.non_consensual": "sexual_content.non_consensual";
177
+ "sexual_content.exploitation": "sexual_content.exploitation";
178
+ "child_safety.sexualization": "child_safety.sexualization";
179
+ "child_safety.grooming": "child_safety.grooming";
180
+ "child_safety.exploitation": "child_safety.exploitation";
181
+ "self_harm.promotion": "self_harm.promotion";
182
+ "self_harm.instruction": "self_harm.instruction";
183
+ "self_harm.imminent_risk": "self_harm.imminent_risk";
184
+ "privacy.personal_information": "privacy.personal_information";
185
+ "privacy.intimate_media": "privacy.intimate_media";
186
+ "privacy.location_exposure": "privacy.location_exposure";
187
+ "commerce.prohibited_item": "commerce.prohibited_item";
188
+ "commerce.counterfeit": "commerce.counterfeit";
189
+ "commerce.misleading_listing": "commerce.misleading_listing";
190
+ "commerce.unsafe_product": "commerce.unsafe_product";
191
+ "platform_abuse.ban_evasion": "platform_abuse.ban_evasion";
192
+ "platform_abuse.report_abuse": "platform_abuse.report_abuse";
193
+ "platform_abuse.automation_abuse": "platform_abuse.automation_abuse";
194
+ "other.policy_specific": "other.policy_specific";
195
+ "other.unclassifiable": "other.unclassifiable";
196
+ }>;
197
+ resourceIds: z.ZodArray<z.ZodString>;
198
+ severity: z.ZodEnum<{
199
+ low: "low";
200
+ medium: "medium";
201
+ high: "high";
202
+ critical: "critical";
203
+ }>;
204
+ context: z.ZodOptional<z.ZodEnum<{
205
+ artistic: "artistic";
206
+ educational: "educational";
207
+ documentary: "documentary";
208
+ newsworthy: "newsworthy";
209
+ satire: "satire";
210
+ counter_speech: "counter_speech";
211
+ medical: "medical";
212
+ consensual: "consensual";
213
+ fictional: "fictional";
214
+ }>>;
215
+ confidence: z.ZodNumber;
216
+ policyRuleIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
217
+ }, z.core.$strict>>;
218
+ recommendedActions: z.ZodArray<z.ZodEnum<{
219
+ remove_or_restrict: "remove_or_restrict";
220
+ allow_with_label: "allow_with_label";
221
+ remove: "remove";
222
+ hide: "hide";
223
+ label: "label";
224
+ age_gate: "age_gate";
225
+ reduce_distribution: "reduce_distribution";
226
+ freeze_transaction: "freeze_transaction";
227
+ suspend_user: "suspend_user";
228
+ request_changes: "request_changes";
229
+ allow: "allow";
230
+ restore: "restore";
231
+ no_action: "no_action";
232
+ request_more_context: "request_more_context";
233
+ hold: "hold";
234
+ local_manual_review: "local_manual_review";
235
+ keep_restricted_temporarily: "keep_restricted_temporarily";
236
+ escalate: "escalate";
237
+ no_global_effect: "no_global_effect";
238
+ specialist_queue: "specialist_queue";
239
+ legal_queue: "legal_queue";
240
+ safety_queue: "safety_queue";
241
+ }>>;
242
+ notes: z.ZodOptional<z.ZodString>;
243
+ }, z.core.$strict>;
244
+ export type ReviewSubmission = z.infer<typeof ReviewSubmissionSchema>;
245
+ /**
246
+ * §4.1: a reviewer may declare a conflict, a language they do not have,
247
+ * material too sensitive for them, or context they cannot obtain — "without
248
+ * penalty". These four are the plan's own list, made structured as §10.3 asks.
249
+ */
250
+ export declare const RECUSAL_REASONS: readonly ["conflict_of_interest", "language", "too_sensitive", "insufficient_context"];
251
+ export declare const RecusalReasonSchema: z.ZodEnum<{
252
+ language: "language";
253
+ insufficient_context: "insufficient_context";
254
+ conflict_of_interest: "conflict_of_interest";
255
+ too_sensitive: "too_sensitive";
256
+ }>;
257
+ export type RecusalReason = z.infer<typeof RecusalReasonSchema>;
258
+ /**
259
+ * The body of `POST /v1/reviewer/assignments/{id}/recuse`.
260
+ *
261
+ * Strict for the same reason as the submission, and with no free-text field
262
+ * beyond a short optional note: a recusal is routing information, and the more
263
+ * it can say about the case, the more it becomes a channel for case content to
264
+ * leak into operational data.
265
+ */
266
+ export declare const RecusalSubmissionSchema: z.ZodObject<{
267
+ reason: z.ZodEnum<{
268
+ language: "language";
269
+ insufficient_context: "insufficient_context";
270
+ conflict_of_interest: "conflict_of_interest";
271
+ too_sensitive: "too_sensitive";
272
+ }>;
273
+ note: z.ZodOptional<z.ZodString>;
274
+ }, z.core.$strict>;
275
+ export type RecusalSubmission = z.infer<typeof RecusalSubmissionSchema>;
276
+ //# sourceMappingURL=reviews.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviews.d.ts","sourceRoot":"","sources":["../src/reviews.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,uFAKlB,CAAC;AACX,eAAO,MAAM,mBAAmB;;;;;EAA0B,CAAC;AAC3D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,yCAA0C,CAAC;AAC7E,eAAO,MAAM,wBAAwB;;;EAAgC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAO9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA+B/B,CAAC;AACL,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,eAAe,wFAKlB,CAAC;AACX,eAAO,MAAM,mBAAmB;;;;;EAA0B,CAAC;AAC3D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;kBAGlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ /**
3
+ * What one reviewer submits (§9.2, §9.3) and how they step away (§4.1).
4
+ *
5
+ * §9.2 splits the form in two — describe the material, then evaluate it against
6
+ * a rule — to reduce anchoring on the reporter's category and to let the same
7
+ * description be reused under different policies. §9.3 then gives one flat
8
+ * result object. Both are true at once: a `ReviewFinding` carries the step-one
9
+ * fields (`code`, `resourceIds`, `severity`, `confidence`) and the step-two
10
+ * fields (`policyRuleIds`), and the enclosing submission carries the step-two
11
+ * verdict (`outcome`, `recommendedActions`). The split is a property of the
12
+ * interface, not of the payload, so it is documented here rather than nested.
13
+ *
14
+ * The submission is `.strict()`, and that is a safety decision, not tidiness.
15
+ * A review belongs to an assignment the server issued; the assignment id comes
16
+ * from the route, the case and the reviewer come from the assignment. If this
17
+ * object tolerated a `caseId`, an `assignmentId` or a `reviewerId`, the day
18
+ * somebody read one of them would be the day a reviewer could vote on a case
19
+ * they were never drawn for — against "nobody chooses the case they review".
20
+ * Strict makes that field impossible rather than merely unused.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.RecusalSubmissionSchema = exports.RecusalReasonSchema = exports.RECUSAL_REASONS = exports.ReviewSubmissionSchema = exports.ReviewFindingSchema = exports.ContextSufficiencySchema = exports.CONTEXT_SUFFICIENCIES = exports.ReviewOutcomeSchema = exports.REVIEW_OUTCOMES = void 0;
24
+ const zod_1 = require("zod");
25
+ const primitives_1 = require("./primitives");
26
+ const policies_1 = require("./policies");
27
+ const resources_1 = require("./resources");
28
+ const taxonomy_1 = require("./taxonomy");
29
+ /**
30
+ * What a single reviewer can conclude.
31
+ *
32
+ * Narrower than §9.6's decision outcomes on purpose. `inconclusive` is what the
33
+ * consensus engine reports when a panel does not agree — a single reviewer
34
+ * cannot fail to agree with themselves, and "the absence of consensus is
35
+ * neither guilt nor innocence" only holds if `inconclusive` is produced by the
36
+ * engine and never voted for. `duplicate`, `escalated` and `superseded` are
37
+ * likewise case-level states, not opinions about material. The plan never
38
+ * enumerates review outcomes; this is that gap resolved toward the invariant.
39
+ */
40
+ exports.REVIEW_OUTCOMES = [
41
+ 'violation',
42
+ 'no_violation',
43
+ 'insufficient_context',
44
+ 'content_unavailable',
45
+ ];
46
+ exports.ReviewOutcomeSchema = zod_1.z.enum(exports.REVIEW_OUTCOMES);
47
+ /** §9.5 feeds this into `contextFactor`: 1.0 if sufficient, 0.5 otherwise. */
48
+ exports.CONTEXT_SUFFICIENCIES = ['sufficient', 'insufficient'];
49
+ exports.ContextSufficiencySchema = zod_1.z.enum(exports.CONTEXT_SUFFICIENCIES);
50
+ /**
51
+ * One structured finding from one reviewer (§9.3).
52
+ *
53
+ * `resourceIds` is required and non-empty: §9.4 makes the affected resource one
54
+ * of the dimensions consensus is measured on, so a finding that does not say
55
+ * what it is about cannot be agreed with or disagreed with.
56
+ *
57
+ * `policyRuleIds` is optional: step one of the form can complete without step
58
+ * two — a reviewer may classify material accurately and find no rule that
59
+ * covers it, which is exactly the `no_violation`-with-findings case §6.2
60
+ * describes.
61
+ *
62
+ * `context` is §9.2's and §9.4's "excepción", written as §6.2 writes it: beside
63
+ * the code and the severity, because "artistic nudity" is a different
64
+ * description of the material from "nudity" rather than a different verdict
65
+ * about it. Optional, and absence means no exception applies.
66
+ *
67
+ * `confidence` communicates quality and triggers escalation (§9.5). It never
68
+ * weights the vote.
69
+ */
70
+ exports.ReviewFindingSchema = zod_1.z.strictObject({
71
+ code: taxonomy_1.TaxonomyCodeSchema,
72
+ resourceIds: zod_1.z.array(resources_1.ResourceIdSchema).min(1).max(primitives_1.CONTRACT_LIMITS.RESOURCE_REFS_PER_FINDING_MAX),
73
+ severity: taxonomy_1.SeveritySchema,
74
+ context: taxonomy_1.FindingContextSchema.optional(),
75
+ confidence: primitives_1.UnitIntervalSchema,
76
+ policyRuleIds: zod_1.z.array(policies_1.PolicyRuleIdSchema).max(primitives_1.CONTRACT_LIMITS.POLICY_RULE_IDS_MAX).optional(),
77
+ });
78
+ /**
79
+ * The body of `POST /v1/reviewer/assignments/{id}/reviews` — §9.3's "result of
80
+ * a review".
81
+ *
82
+ * `recommendedActions` is a list of action tokens here, where a decision's
83
+ * `recommendedActions` are objects that name their target resources. That
84
+ * difference is in the plan (§9.3 versus Appendix B) and is kept: a reviewer
85
+ * recommends a course of action, and the consensus process is what binds an
86
+ * agreed recommendation to the specific resources it applies to.
87
+ */
88
+ exports.ReviewSubmissionSchema = zod_1.z
89
+ .strictObject({
90
+ outcome: exports.ReviewOutcomeSchema,
91
+ contextSufficiency: exports.ContextSufficiencySchema,
92
+ findings: zod_1.z.array(exports.ReviewFindingSchema).max(primitives_1.CONTRACT_LIMITS.FINDINGS_MAX),
93
+ recommendedActions: zod_1.z
94
+ .array(taxonomy_1.RecommendedActionSchema)
95
+ .max(primitives_1.CONTRACT_LIMITS.RECOMMENDED_ACTIONS_MAX),
96
+ /**
97
+ * §13.5 and the "sensitive content never reaches logs" invariant apply to
98
+ * this field as much as to the evidence: a note is free text a human wrote
99
+ * while looking at the material, so it is bounded here and must be treated
100
+ * as case content everywhere downstream — never logged, never attested.
101
+ */
102
+ notes: zod_1.z.string().max(primitives_1.CONTRACT_LIMITS.LONG_TEXT_MAX_LENGTH).optional(),
103
+ })
104
+ .superRefine((review, ctx) => {
105
+ if (review.outcome === 'insufficient_context' && review.contextSufficiency !== 'insufficient') {
106
+ ctx.addIssue({
107
+ code: 'custom',
108
+ path: ['contextSufficiency'],
109
+ message: 'an outcome of insufficient_context requires contextSufficiency "insufficient"',
110
+ });
111
+ }
112
+ if (review.outcome === 'violation' && review.findings.length === 0) {
113
+ ctx.addIssue({
114
+ code: 'custom',
115
+ path: ['findings'],
116
+ message: 'a violation outcome requires at least one finding',
117
+ });
118
+ }
119
+ });
120
+ /**
121
+ * §4.1: a reviewer may declare a conflict, a language they do not have,
122
+ * material too sensitive for them, or context they cannot obtain — "without
123
+ * penalty". These four are the plan's own list, made structured as §10.3 asks.
124
+ */
125
+ exports.RECUSAL_REASONS = [
126
+ 'conflict_of_interest',
127
+ 'language',
128
+ 'too_sensitive',
129
+ 'insufficient_context',
130
+ ];
131
+ exports.RecusalReasonSchema = zod_1.z.enum(exports.RECUSAL_REASONS);
132
+ /**
133
+ * The body of `POST /v1/reviewer/assignments/{id}/recuse`.
134
+ *
135
+ * Strict for the same reason as the submission, and with no free-text field
136
+ * beyond a short optional note: a recusal is routing information, and the more
137
+ * it can say about the case, the more it becomes a channel for case content to
138
+ * leak into operational data.
139
+ */
140
+ exports.RecusalSubmissionSchema = zod_1.z.strictObject({
141
+ reason: exports.RecusalReasonSchema,
142
+ note: zod_1.z.string().max(primitives_1.CONTRACT_LIMITS.SHORT_TEXT_MAX_LENGTH).optional(),
143
+ });
144
+ //# sourceMappingURL=reviews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviews.js","sourceRoot":"","sources":["../src/reviews.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAEH,6BAAwB;AAExB,6CAAmE;AACnE,yCAAgD;AAChD,2CAA+C;AAC/C,yCAKoB;AAEpB;;;;;;;;;;GAUG;AACU,QAAA,eAAe,GAAG;IAC7B,WAAW;IACX,cAAc;IACd,sBAAsB;IACtB,qBAAqB;CACb,CAAC;AACE,QAAA,mBAAmB,GAAG,OAAC,CAAC,IAAI,CAAC,uBAAe,CAAC,CAAC;AAG3D,8EAA8E;AACjE,QAAA,qBAAqB,GAAG,CAAC,YAAY,EAAE,cAAc,CAAU,CAAC;AAChE,QAAA,wBAAwB,GAAG,OAAC,CAAC,IAAI,CAAC,6BAAqB,CAAC,CAAC;AAGtE;;;;;;;;;;;;;;;;;;;GAmBG;AACU,QAAA,mBAAmB,GAAG,OAAC,CAAC,YAAY,CAAC;IAChD,IAAI,EAAE,6BAAkB;IACxB,WAAW,EAAE,OAAC,CAAC,KAAK,CAAC,4BAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,4BAAe,CAAC,6BAA6B,CAAC;IAChG,QAAQ,EAAE,yBAAc;IACxB,OAAO,EAAE,+BAAoB,CAAC,QAAQ,EAAE;IACxC,UAAU,EAAE,+BAAkB;IAC9B,aAAa,EAAE,OAAC,CAAC,KAAK,CAAC,6BAAkB,CAAC,CAAC,GAAG,CAAC,4BAAe,CAAC,mBAAmB,CAAC,CAAC,QAAQ,EAAE;CAC/F,CAAC,CAAC;AAGH;;;;;;;;;GASG;AACU,QAAA,sBAAsB,GAAG,OAAC;KACpC,YAAY,CAAC;IACZ,OAAO,EAAE,2BAAmB;IAC5B,kBAAkB,EAAE,gCAAwB;IAC5C,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,2BAAmB,CAAC,CAAC,GAAG,CAAC,4BAAe,CAAC,YAAY,CAAC;IACxE,kBAAkB,EAAE,OAAC;SAClB,KAAK,CAAC,kCAAuB,CAAC;SAC9B,GAAG,CAAC,4BAAe,CAAC,uBAAuB,CAAC;IAC/C;;;;;OAKG;IACH,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,4BAAe,CAAC,oBAAoB,CAAC,CAAC,QAAQ,EAAE;CACvE,CAAC;KACD,WAAW,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IAC3B,IAAI,MAAM,CAAC,OAAO,KAAK,sBAAsB,IAAI,MAAM,CAAC,kBAAkB,KAAK,cAAc,EAAE,CAAC;QAC9F,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,oBAAoB,CAAC;YAC5B,OAAO,EAAE,+EAA+E;SACzF,CAAC,CAAC;IACL,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,UAAU,CAAC;YAClB,OAAO,EAAE,mDAAmD;SAC7D,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAGL;;;;GAIG;AACU,QAAA,eAAe,GAAG;IAC7B,sBAAsB;IACtB,UAAU;IACV,eAAe;IACf,sBAAsB;CACd,CAAC;AACE,QAAA,mBAAmB,GAAG,OAAC,CAAC,IAAI,CAAC,uBAAe,CAAC,CAAC;AAG3D;;;;;;;GAOG;AACU,QAAA,uBAAuB,GAAG,OAAC,CAAC,YAAY,CAAC;IACpD,MAAM,EAAE,2BAAmB;IAC3B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,4BAAe,CAAC,qBAAqB,CAAC,CAAC,QAAQ,EAAE;CACvE,CAAC,CAAC"}