@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,353 @@
1
+ /**
2
+ * Decisions (§9.6, §9.9, Appendix B).
3
+ *
4
+ * "A published decision is never edited, only superseded." No parse can see a
5
+ * second write, so schema-level immutability would be theatre — and a shallow
6
+ * `Object.freeze` on the parsed result would be worse than theatre, since it
7
+ * would leave `findings` mutable while looking like a guarantee. Immutability
8
+ * is enforced where it is real: the store never updates a published revision.
9
+ *
10
+ * What the schema CAN enforce is the shape of the supersession chain, and it
11
+ * does: revision 1 supersedes nothing, and every later revision names what it
12
+ * replaced. §9.9's worked example is exactly that, and a revision 2 that
13
+ * supersedes nothing would be an edit wearing a new number.
14
+ *
15
+ * Decisions travel outbound, to tenants and to the reviewer app, so they are
16
+ * `.loose()`: §10.11 requires unknown fields not to break clients, and passing
17
+ * them through rather than stripping them means a client that persists a
18
+ * decision keeps whatever a newer CrowdSource added. This is the opposite of
19
+ * the inbound rule in `case-envelope.ts`, for the opposite reason.
20
+ */
21
+ import { z } from 'zod';
22
+ /** §3.2 decision states. */
23
+ export declare const DECISION_STATUSES: readonly ["provisional", "final", "superseded", "corrected"];
24
+ export declare const DecisionStatusSchema: z.ZodEnum<{
25
+ provisional: "provisional";
26
+ final: "final";
27
+ superseded: "superseded";
28
+ corrected: "corrected";
29
+ }>;
30
+ export type DecisionStatus = z.infer<typeof DecisionStatusSchema>;
31
+ /**
32
+ * §9.6 outcomes.
33
+ *
34
+ * `inconclusive` is its own outcome and must never collapse into
35
+ * `no_violation`: a jury that reviewed the case and did not reach the threshold
36
+ * has said something different from a jury that agreed nothing was wrong. Both
37
+ * are here, distinctly, and no code in this package maps one to the other.
38
+ */
39
+ export declare const DECISION_OUTCOMES: readonly ["violation", "no_violation", "insufficient_context", "inconclusive", "content_unavailable", "duplicate", "escalated"];
40
+ export declare const DecisionOutcomeSchema: z.ZodEnum<{
41
+ violation: "violation";
42
+ no_violation: "no_violation";
43
+ insufficient_context: "insufficient_context";
44
+ content_unavailable: "content_unavailable";
45
+ inconclusive: "inconclusive";
46
+ duplicate: "duplicate";
47
+ escalated: "escalated";
48
+ }>;
49
+ export type DecisionOutcome = z.infer<typeof DecisionOutcomeSchema>;
50
+ /**
51
+ * A confirmed finding (Appendix B).
52
+ *
53
+ * Carries two fields a `ReviewFinding` does not. `scope` says how far the
54
+ * finding reaches and is what §11.7.5 gates an Oxy Trust effect on;
55
+ * `attribution` names whose conduct it is. Neither belongs on an individual
56
+ * review: a reviewer classifies material, and it is the consensus process that
57
+ * decides the classification is confirmed and therefore attributable.
58
+ *
59
+ * `attribution` is optional because plenty of confirmed findings attribute
60
+ * nothing to anybody — material can violate a rule without any principal having
61
+ * behaved badly.
62
+ *
63
+ * `context` carries §6.2's exception through unchanged from the reviews that
64
+ * agreed on it. It is one of §9.4's six consensus dimensions, so a published
65
+ * finding that dropped it would be a decision nobody could reproduce from the
66
+ * ballots.
67
+ */
68
+ export declare const DecisionFindingSchema: 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
+ scope: z.ZodEnum<{
130
+ application_local: "application_local";
131
+ oxy_network: "oxy_network";
132
+ identity_integrity: "identity_integrity";
133
+ }>;
134
+ attribution: z.ZodOptional<z.ZodEnum<{
135
+ author: "author";
136
+ reporter: "reporter";
137
+ reviewer: "reviewer";
138
+ }>>;
139
+ policyRuleIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
140
+ }, z.core.$loose>;
141
+ export type DecisionFinding = z.infer<typeof DecisionFindingSchema>;
142
+ /**
143
+ * A recommended action bound to what it applies to (Appendix B).
144
+ *
145
+ * §10.7's webhook example writes recommended actions as bare strings, the way
146
+ * §9.3 writes them for a single review. Appendix B — the reference Decision —
147
+ * writes them as objects. The object form wins for decisions: a decision that
148
+ * recommends removal without saying what to remove is not actionable, and an
149
+ * application that acts on it is guessing. The string form survives where the
150
+ * plan uses it, on a review.
151
+ *
152
+ * `targetResourceIds` is optional because some actions have no target —
153
+ * `escalate`, `no_global_effect` and `no_action` are about the case, not about
154
+ * a resource.
155
+ */
156
+ export declare const DecisionRecommendedActionSchema: z.ZodObject<{
157
+ action: z.ZodEnum<{
158
+ remove_or_restrict: "remove_or_restrict";
159
+ allow_with_label: "allow_with_label";
160
+ remove: "remove";
161
+ hide: "hide";
162
+ label: "label";
163
+ age_gate: "age_gate";
164
+ reduce_distribution: "reduce_distribution";
165
+ freeze_transaction: "freeze_transaction";
166
+ suspend_user: "suspend_user";
167
+ request_changes: "request_changes";
168
+ allow: "allow";
169
+ restore: "restore";
170
+ no_action: "no_action";
171
+ request_more_context: "request_more_context";
172
+ hold: "hold";
173
+ local_manual_review: "local_manual_review";
174
+ keep_restricted_temporarily: "keep_restricted_temporarily";
175
+ escalate: "escalate";
176
+ no_global_effect: "no_global_effect";
177
+ specialist_queue: "specialist_queue";
178
+ legal_queue: "legal_queue";
179
+ safety_queue: "safety_queue";
180
+ }>;
181
+ targetResourceIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
182
+ }, z.core.$loose>;
183
+ export type DecisionRecommendedAction = z.infer<typeof DecisionRecommendedActionSchema>;
184
+ /**
185
+ * The panel that produced the decision (Appendix B).
186
+ *
187
+ * The arithmetic is checked here because it is the auditable trace of "one
188
+ * qualified person, one vote": `agreement` must equal
189
+ * `winningVotes / decisiveVotes` (§9.5), and the counts must nest. If a
190
+ * weighting ever crept into the engine, `agreement` would stop matching the
191
+ * count of people, and this is where that shows up.
192
+ *
193
+ * `size` is not restricted to {3, 5, 7}. Those are §9.4's community panel
194
+ * sizes, but §9.4 also routes critical categories to specialist pools that do
195
+ * not use the standard jury at all, and an appeal panel is only bounded below.
196
+ */
197
+ export declare const DecisionJurySchema: z.ZodObject<{
198
+ size: z.ZodNumber;
199
+ decisiveVotes: z.ZodNumber;
200
+ winningVotes: z.ZodNumber;
201
+ agreement: z.ZodNumber;
202
+ specialistPresent: z.ZodBoolean;
203
+ }, z.core.$loose>;
204
+ export type DecisionJury = z.infer<typeof DecisionJurySchema>;
205
+ /**
206
+ * One immutable revision of a case's outcome (Appendix B).
207
+ *
208
+ * Every field except `supersedesDecisionId` is required, following §12.8, which
209
+ * marks `supersedes_decision_id` as the only nullable column on the decisions
210
+ * table. The DTO uses that column's name; §9.9's prose sketch writes the same
211
+ * link as `supersedes`.
212
+ */
213
+ export declare const DecisionSchema: z.ZodObject<{
214
+ id: z.ZodString;
215
+ caseId: z.ZodString;
216
+ revision: z.ZodNumber;
217
+ status: z.ZodEnum<{
218
+ provisional: "provisional";
219
+ final: "final";
220
+ superseded: "superseded";
221
+ corrected: "corrected";
222
+ }>;
223
+ outcome: z.ZodEnum<{
224
+ violation: "violation";
225
+ no_violation: "no_violation";
226
+ insufficient_context: "insufficient_context";
227
+ content_unavailable: "content_unavailable";
228
+ inconclusive: "inconclusive";
229
+ duplicate: "duplicate";
230
+ escalated: "escalated";
231
+ }>;
232
+ contextSufficiency: z.ZodEnum<{
233
+ sufficient: "sufficient";
234
+ insufficient: "insufficient";
235
+ }>;
236
+ confidence: z.ZodNumber;
237
+ findings: z.ZodArray<z.ZodObject<{
238
+ code: z.ZodEnum<{
239
+ "integrity.spam": "integrity.spam";
240
+ "integrity.scam": "integrity.scam";
241
+ "integrity.fraud": "integrity.fraud";
242
+ "integrity.impersonation": "integrity.impersonation";
243
+ "integrity.coordinated_manipulation": "integrity.coordinated_manipulation";
244
+ "harassment.insult": "harassment.insult";
245
+ "harassment.targeted_abuse": "harassment.targeted_abuse";
246
+ "harassment.sexual_harassment": "harassment.sexual_harassment";
247
+ "harassment.doxxing": "harassment.doxxing";
248
+ "harassment.credible_threat": "harassment.credible_threat";
249
+ "hate.dehumanization": "hate.dehumanization";
250
+ "hate.slur": "hate.slur";
251
+ "hate.incitement": "hate.incitement";
252
+ "hate.protected_targeting": "hate.protected_targeting";
253
+ "violence.graphic": "violence.graphic";
254
+ "violence.threat": "violence.threat";
255
+ "violence.instruction": "violence.instruction";
256
+ "violence.celebration": "violence.celebration";
257
+ "sexual_content.nudity": "sexual_content.nudity";
258
+ "sexual_content.explicit_activity": "sexual_content.explicit_activity";
259
+ "sexual_content.non_consensual": "sexual_content.non_consensual";
260
+ "sexual_content.exploitation": "sexual_content.exploitation";
261
+ "child_safety.sexualization": "child_safety.sexualization";
262
+ "child_safety.grooming": "child_safety.grooming";
263
+ "child_safety.exploitation": "child_safety.exploitation";
264
+ "self_harm.promotion": "self_harm.promotion";
265
+ "self_harm.instruction": "self_harm.instruction";
266
+ "self_harm.imminent_risk": "self_harm.imminent_risk";
267
+ "privacy.personal_information": "privacy.personal_information";
268
+ "privacy.intimate_media": "privacy.intimate_media";
269
+ "privacy.location_exposure": "privacy.location_exposure";
270
+ "commerce.prohibited_item": "commerce.prohibited_item";
271
+ "commerce.counterfeit": "commerce.counterfeit";
272
+ "commerce.misleading_listing": "commerce.misleading_listing";
273
+ "commerce.unsafe_product": "commerce.unsafe_product";
274
+ "platform_abuse.ban_evasion": "platform_abuse.ban_evasion";
275
+ "platform_abuse.report_abuse": "platform_abuse.report_abuse";
276
+ "platform_abuse.automation_abuse": "platform_abuse.automation_abuse";
277
+ "other.policy_specific": "other.policy_specific";
278
+ "other.unclassifiable": "other.unclassifiable";
279
+ }>;
280
+ resourceIds: z.ZodArray<z.ZodString>;
281
+ severity: z.ZodEnum<{
282
+ low: "low";
283
+ medium: "medium";
284
+ high: "high";
285
+ critical: "critical";
286
+ }>;
287
+ context: z.ZodOptional<z.ZodEnum<{
288
+ artistic: "artistic";
289
+ educational: "educational";
290
+ documentary: "documentary";
291
+ newsworthy: "newsworthy";
292
+ satire: "satire";
293
+ counter_speech: "counter_speech";
294
+ medical: "medical";
295
+ consensual: "consensual";
296
+ fictional: "fictional";
297
+ }>>;
298
+ scope: z.ZodEnum<{
299
+ application_local: "application_local";
300
+ oxy_network: "oxy_network";
301
+ identity_integrity: "identity_integrity";
302
+ }>;
303
+ attribution: z.ZodOptional<z.ZodEnum<{
304
+ author: "author";
305
+ reporter: "reporter";
306
+ reviewer: "reviewer";
307
+ }>>;
308
+ policyRuleIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
309
+ }, z.core.$loose>>;
310
+ recommendedActions: z.ZodArray<z.ZodObject<{
311
+ action: z.ZodEnum<{
312
+ remove_or_restrict: "remove_or_restrict";
313
+ allow_with_label: "allow_with_label";
314
+ remove: "remove";
315
+ hide: "hide";
316
+ label: "label";
317
+ age_gate: "age_gate";
318
+ reduce_distribution: "reduce_distribution";
319
+ freeze_transaction: "freeze_transaction";
320
+ suspend_user: "suspend_user";
321
+ request_changes: "request_changes";
322
+ allow: "allow";
323
+ restore: "restore";
324
+ no_action: "no_action";
325
+ request_more_context: "request_more_context";
326
+ hold: "hold";
327
+ local_manual_review: "local_manual_review";
328
+ keep_restricted_temporarily: "keep_restricted_temporarily";
329
+ escalate: "escalate";
330
+ no_global_effect: "no_global_effect";
331
+ specialist_queue: "specialist_queue";
332
+ legal_queue: "legal_queue";
333
+ safety_queue: "safety_queue";
334
+ }>;
335
+ targetResourceIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
336
+ }, z.core.$loose>>;
337
+ jury: z.ZodObject<{
338
+ size: z.ZodNumber;
339
+ decisiveVotes: z.ZodNumber;
340
+ winningVotes: z.ZodNumber;
341
+ agreement: z.ZodNumber;
342
+ specialistPresent: z.ZodBoolean;
343
+ }, z.core.$loose>;
344
+ policyVersions: z.ZodObject<{
345
+ taxonomy: z.ZodString;
346
+ application: z.ZodString;
347
+ oxyConduct: z.ZodString;
348
+ }, z.core.$loose>;
349
+ supersedesDecisionId: z.ZodOptional<z.ZodString>;
350
+ publishedAt: z.ZodISODateTime;
351
+ }, z.core.$loose>;
352
+ export type Decision = z.infer<typeof DecisionSchema>;
353
+ //# sourceMappingURL=decisions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisions.d.ts","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAexB,4BAA4B;AAC5B,eAAO,MAAM,iBAAiB,8DAA+D,CAAC;AAC9F,eAAO,MAAM,oBAAoB;;;;;EAA4B,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,iIAQpB,CAAC;AACX,eAAO,MAAM,qBAAqB;;;;;;;;EAA4B,CAAC;AAC/D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAKxF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB;;;;;;iBAkC3B,CAAC;AACL,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCvB,CAAC;AACL,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ /**
3
+ * Decisions (§9.6, §9.9, Appendix B).
4
+ *
5
+ * "A published decision is never edited, only superseded." No parse can see a
6
+ * second write, so schema-level immutability would be theatre — and a shallow
7
+ * `Object.freeze` on the parsed result would be worse than theatre, since it
8
+ * would leave `findings` mutable while looking like a guarantee. Immutability
9
+ * is enforced where it is real: the store never updates a published revision.
10
+ *
11
+ * What the schema CAN enforce is the shape of the supersession chain, and it
12
+ * does: revision 1 supersedes nothing, and every later revision names what it
13
+ * replaced. §9.9's worked example is exactly that, and a revision 2 that
14
+ * supersedes nothing would be an edit wearing a new number.
15
+ *
16
+ * Decisions travel outbound, to tenants and to the reviewer app, so they are
17
+ * `.loose()`: §10.11 requires unknown fields not to break clients, and passing
18
+ * them through rather than stripping them means a client that persists a
19
+ * decision keeps whatever a newer CrowdSource added. This is the opposite of
20
+ * the inbound rule in `case-envelope.ts`, for the opposite reason.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.DecisionSchema = exports.DecisionJurySchema = exports.DecisionRecommendedActionSchema = exports.DecisionFindingSchema = exports.DecisionOutcomeSchema = exports.DECISION_OUTCOMES = exports.DecisionStatusSchema = exports.DECISION_STATUSES = 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 reviews_1 = require("./reviews");
29
+ const taxonomy_1 = require("./taxonomy");
30
+ /** §3.2 decision states. */
31
+ exports.DECISION_STATUSES = ['provisional', 'final', 'superseded', 'corrected'];
32
+ exports.DecisionStatusSchema = zod_1.z.enum(exports.DECISION_STATUSES);
33
+ /**
34
+ * §9.6 outcomes.
35
+ *
36
+ * `inconclusive` is its own outcome and must never collapse into
37
+ * `no_violation`: a jury that reviewed the case and did not reach the threshold
38
+ * has said something different from a jury that agreed nothing was wrong. Both
39
+ * are here, distinctly, and no code in this package maps one to the other.
40
+ */
41
+ exports.DECISION_OUTCOMES = [
42
+ 'violation',
43
+ 'no_violation',
44
+ 'insufficient_context',
45
+ 'inconclusive',
46
+ 'content_unavailable',
47
+ 'duplicate',
48
+ 'escalated',
49
+ ];
50
+ exports.DecisionOutcomeSchema = zod_1.z.enum(exports.DECISION_OUTCOMES);
51
+ /**
52
+ * A confirmed finding (Appendix B).
53
+ *
54
+ * Carries two fields a `ReviewFinding` does not. `scope` says how far the
55
+ * finding reaches and is what §11.7.5 gates an Oxy Trust effect on;
56
+ * `attribution` names whose conduct it is. Neither belongs on an individual
57
+ * review: a reviewer classifies material, and it is the consensus process that
58
+ * decides the classification is confirmed and therefore attributable.
59
+ *
60
+ * `attribution` is optional because plenty of confirmed findings attribute
61
+ * nothing to anybody — material can violate a rule without any principal having
62
+ * behaved badly.
63
+ *
64
+ * `context` carries §6.2's exception through unchanged from the reviews that
65
+ * agreed on it. It is one of §9.4's six consensus dimensions, so a published
66
+ * finding that dropped it would be a decision nobody could reproduce from the
67
+ * ballots.
68
+ */
69
+ exports.DecisionFindingSchema = zod_1.z.looseObject({
70
+ code: taxonomy_1.TaxonomyCodeSchema,
71
+ resourceIds: zod_1.z.array(resources_1.ResourceIdSchema).min(1).max(primitives_1.CONTRACT_LIMITS.RESOURCE_REFS_PER_FINDING_MAX),
72
+ severity: taxonomy_1.SeveritySchema,
73
+ context: taxonomy_1.FindingContextSchema.optional(),
74
+ scope: taxonomy_1.FindingScopeSchema,
75
+ attribution: taxonomy_1.FindingAttributionSchema.optional(),
76
+ policyRuleIds: zod_1.z.array(policies_1.PolicyRuleIdSchema).max(primitives_1.CONTRACT_LIMITS.POLICY_RULE_IDS_MAX).optional(),
77
+ });
78
+ /**
79
+ * A recommended action bound to what it applies to (Appendix B).
80
+ *
81
+ * §10.7's webhook example writes recommended actions as bare strings, the way
82
+ * §9.3 writes them for a single review. Appendix B — the reference Decision —
83
+ * writes them as objects. The object form wins for decisions: a decision that
84
+ * recommends removal without saying what to remove is not actionable, and an
85
+ * application that acts on it is guessing. The string form survives where the
86
+ * plan uses it, on a review.
87
+ *
88
+ * `targetResourceIds` is optional because some actions have no target —
89
+ * `escalate`, `no_global_effect` and `no_action` are about the case, not about
90
+ * a resource.
91
+ */
92
+ exports.DecisionRecommendedActionSchema = zod_1.z.looseObject({
93
+ action: taxonomy_1.RecommendedActionSchema,
94
+ targetResourceIds: zod_1.z
95
+ .array(resources_1.ResourceIdSchema)
96
+ .max(primitives_1.CONTRACT_LIMITS.RESOURCE_REFS_PER_FINDING_MAX)
97
+ .optional(),
98
+ });
99
+ /** Floating point: `winningVotes / decisiveVotes` will not be exact. */
100
+ const AGREEMENT_TOLERANCE = 1e-6;
101
+ /**
102
+ * The panel that produced the decision (Appendix B).
103
+ *
104
+ * The arithmetic is checked here because it is the auditable trace of "one
105
+ * qualified person, one vote": `agreement` must equal
106
+ * `winningVotes / decisiveVotes` (§9.5), and the counts must nest. If a
107
+ * weighting ever crept into the engine, `agreement` would stop matching the
108
+ * count of people, and this is where that shows up.
109
+ *
110
+ * `size` is not restricted to {3, 5, 7}. Those are §9.4's community panel
111
+ * sizes, but §9.4 also routes critical categories to specialist pools that do
112
+ * not use the standard jury at all, and an appeal panel is only bounded below.
113
+ */
114
+ exports.DecisionJurySchema = zod_1.z
115
+ .looseObject({
116
+ size: zod_1.z.number().int().positive().max(primitives_1.CONTRACT_LIMITS.JURY_SIZE_MAX),
117
+ /** Reviews that expressed a decisive opinion — the denominator of §9.5. */
118
+ decisiveVotes: zod_1.z.number().int().positive().max(primitives_1.CONTRACT_LIMITS.JURY_SIZE_MAX),
119
+ winningVotes: zod_1.z.number().int().nonnegative().max(primitives_1.CONTRACT_LIMITS.JURY_SIZE_MAX),
120
+ agreement: primitives_1.UnitIntervalSchema,
121
+ specialistPresent: zod_1.z.boolean(),
122
+ })
123
+ .superRefine((jury, ctx) => {
124
+ if (jury.decisiveVotes > jury.size) {
125
+ ctx.addIssue({
126
+ code: 'custom',
127
+ path: ['decisiveVotes'],
128
+ message: 'decisiveVotes cannot exceed the panel size',
129
+ });
130
+ return;
131
+ }
132
+ if (jury.winningVotes > jury.decisiveVotes) {
133
+ ctx.addIssue({
134
+ code: 'custom',
135
+ path: ['winningVotes'],
136
+ message: 'winningVotes cannot exceed decisiveVotes',
137
+ });
138
+ return;
139
+ }
140
+ const expected = jury.winningVotes / jury.decisiveVotes;
141
+ if (Math.abs(jury.agreement - expected) > AGREEMENT_TOLERANCE) {
142
+ ctx.addIssue({
143
+ code: 'custom',
144
+ path: ['agreement'],
145
+ message: `agreement must equal winningVotes / decisiveVotes (${expected})`,
146
+ });
147
+ }
148
+ });
149
+ /**
150
+ * One immutable revision of a case's outcome (Appendix B).
151
+ *
152
+ * Every field except `supersedesDecisionId` is required, following §12.8, which
153
+ * marks `supersedes_decision_id` as the only nullable column on the decisions
154
+ * table. The DTO uses that column's name; §9.9's prose sketch writes the same
155
+ * link as `supersedes`.
156
+ */
157
+ exports.DecisionSchema = zod_1.z
158
+ .looseObject({
159
+ id: primitives_1.IdentifierSchema,
160
+ caseId: primitives_1.IdentifierSchema,
161
+ revision: zod_1.z.number().int().positive(),
162
+ status: exports.DecisionStatusSchema,
163
+ outcome: exports.DecisionOutcomeSchema,
164
+ contextSufficiency: reviews_1.ContextSufficiencySchema,
165
+ confidence: primitives_1.UnitIntervalSchema,
166
+ findings: zod_1.z.array(exports.DecisionFindingSchema).max(primitives_1.CONTRACT_LIMITS.FINDINGS_MAX),
167
+ recommendedActions: zod_1.z
168
+ .array(exports.DecisionRecommendedActionSchema)
169
+ .max(primitives_1.CONTRACT_LIMITS.RECOMMENDED_ACTIONS_MAX),
170
+ jury: exports.DecisionJurySchema,
171
+ policyVersions: policies_1.DecisionPolicyVersionsSchema,
172
+ supersedesDecisionId: primitives_1.IdentifierSchema.optional(),
173
+ publishedAt: primitives_1.TimestampSchema,
174
+ })
175
+ .superRefine((decision, ctx) => {
176
+ if (decision.revision === 1 && decision.supersedesDecisionId !== undefined) {
177
+ ctx.addIssue({
178
+ code: 'custom',
179
+ path: ['supersedesDecisionId'],
180
+ message: 'the first revision of a case supersedes nothing',
181
+ });
182
+ }
183
+ if (decision.revision > 1 && decision.supersedesDecisionId === undefined) {
184
+ ctx.addIssue({
185
+ code: 'custom',
186
+ path: ['supersedesDecisionId'],
187
+ message: 'a revision after the first must name the decision it supersedes',
188
+ });
189
+ }
190
+ if (decision.outcome === 'violation' && decision.findings.length === 0) {
191
+ ctx.addIssue({
192
+ code: 'custom',
193
+ path: ['findings'],
194
+ message: 'a violation outcome requires at least one finding',
195
+ });
196
+ }
197
+ });
198
+ //# sourceMappingURL=decisions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisions.js","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Public entry point for `@oxyhq/crowdsource-contracts`.
3
+ *
4
+ * The contracts every CrowdSource surface agrees on: the backend, the reviewer
5
+ * and console clients, the published SDKs, and third-party integrators. One
6
+ * entry point because the package publishes one `exports` path — this is the
7
+ * package boundary, not a convenience barrel over an internal tree.
8
+ *
9
+ * Two version numbers live here and mean different things. `/v1` is the route
10
+ * prefix and is not this package's business. `CASE_ENVELOPE_SCHEMA_VERSION`
11
+ * travels inside the payload and is validated separately (§10.11). Additive
12
+ * changes bump neither.
13
+ *
14
+ * Where strictness lands, and why, since it is the one thing a reader will want
15
+ * to look up:
16
+ *
17
+ * * **Inbound from a tenant or a reviewer — strict.** The Case Envelope tree,
18
+ * the review submission, the recusal, the policy set, the resource schema
19
+ * registration. A dropped field here is context a jury never sees or an
20
+ * input nobody reviewed, and §10.11 makes the exception explicitly for
21
+ * fields "the schema forbids for safety".
22
+ * * **Outbound to a tenant — loose.** Decisions, webhook envelopes and event
23
+ * payloads pass unknown fields through, so a newer CrowdSource never breaks
24
+ * an older client and a receiver that persists `event.data` keeps all of it.
25
+ * * **Internal, to Oxy Trust — strict.** The reputation event carries no
26
+ * resource ids and no free text on purpose; an unrecognised field is how
27
+ * content reaches a reputation ledger. The `.v1` in the event type is what
28
+ * handles evolution there.
29
+ *
30
+ * Open bags (`metadata`, custom payloads, registered JSON Schemas) are the
31
+ * deliberate exception in both directions: open by definition, but flat or
32
+ * depth-bounded, scalar-typed, key-restricted and free of prototype-bearing
33
+ * names.
34
+ */
35
+ export * from './primitives';
36
+ export * from './taxonomy';
37
+ export * from './policies';
38
+ export * from './resources';
39
+ export * from './case-envelope';
40
+ export * from './reviews';
41
+ export * from './decisions';
42
+ export * from './webhooks';
43
+ export * from './reputation-events';
44
+ export * from './json-schema';
45
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC"}