jansathi-community-schema 0.50.2 → 0.51.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.
- package/dist/social-score/kinds.d.ts +2 -1
- package/dist/social-score/kinds.d.ts.map +1 -1
- package/dist/social-score/kinds.js +1 -0
- package/dist/social-score/kinds.js.map +1 -1
- package/dist/social-score/rules.d.ts.map +1 -1
- package/dist/social-score/rules.js +21 -2
- package/dist/social-score/rules.js.map +1 -1
- package/dist/social-score/wire.d.ts +2 -0
- package/dist/social-score/wire.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* @module community-schema/social-score/kinds
|
|
14
14
|
*/
|
|
15
15
|
import { z } from 'zod';
|
|
16
|
-
export declare const SCORE_KIND_VALUES: readonly ["udp_approved", "crowdfunding_donation_verified", "crowdfunding_campaign_succeeded", "item_donated", "lost_found_resolved", "suggestions_complaints_submitted", "suggestions_complaints_resolved", "resource_returned_well", "item_pickup_to_center", "udp_initiated", "udp_completed", "udp_verified", "event_organized", "poll_created", "poll_voted", "post_qualified", "poll_qualified", "crowdfunding_qualified", "suggestions_complaints_qualified", "comment_qualified", "reform_role_granted", "leader_role_granted", "leader_added_volunteer", "leader_mentee_reached_milestone", "official_community_run"];
|
|
16
|
+
export declare const SCORE_KIND_VALUES: readonly ["udp_step_completed", "udp_approved", "crowdfunding_donation_verified", "crowdfunding_campaign_succeeded", "item_donated", "lost_found_resolved", "suggestions_complaints_submitted", "suggestions_complaints_resolved", "resource_returned_well", "item_pickup_to_center", "udp_initiated", "udp_completed", "udp_verified", "event_organized", "poll_created", "poll_voted", "post_qualified", "poll_qualified", "crowdfunding_qualified", "suggestions_complaints_qualified", "comment_qualified", "reform_role_granted", "leader_role_granted", "leader_added_volunteer", "leader_mentee_reached_milestone", "official_community_run"];
|
|
17
17
|
export type ScoreKind = (typeof SCORE_KIND_VALUES)[number];
|
|
18
18
|
export declare const scoreKindSchema: z.ZodEnum<{
|
|
19
|
+
udp_step_completed: "udp_step_completed";
|
|
19
20
|
udp_approved: "udp_approved";
|
|
20
21
|
crowdfunding_donation_verified: "crowdfunding_donation_verified";
|
|
21
22
|
crowdfunding_campaign_succeeded: "crowdfunding_campaign_succeeded";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kinds.d.ts","sourceRoot":"","sources":["../../src/social-score/kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"kinds.d.ts","sourceRoot":"","sources":["../../src/social-score/kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB,snBAiCpB,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kinds.js","sourceRoot":"","sources":["../../src/social-score/kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,UAAU;IACV,cAAc;IACd,qBAAqB;IACrB,gCAAgC;IAChC,iCAAiC;IACjC,cAAc;IACd,qBAAqB;IACrB,kCAAkC;IAClC,iCAAiC;IACjC,wBAAwB;IACxB,6CAA6C;IAC7C,uBAAuB;IACvB,eAAe;IACf,eAAe;IACf,cAAc;IACd,aAAa;IACb,iBAAiB;IACjB,cAAc;IACd,YAAY;IACZ,6DAA6D;IAC7D,gBAAgB;IAChB,gBAAgB;IAChB,wBAAwB;IACxB,kCAAkC;IAClC,mBAAmB;IACnB,aAAa;IACb,qBAAqB;IACrB,qBAAqB;IACrB,wBAAwB;IACxB,iCAAiC;IACjC,wBAAwB;CAChB,CAAC;AAIX,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC","sourcesContent":["/**\n * Social-score KINDS — the canonical enumeration of every action that\n * can emit points. Single source of truth shared by the backend\n * emitter + rule table and any frontend that reasons about a specific\n * kind (e.g. a \"New\" chip keyed on a kind, or the recent-activity row\n * shape on the breakdown page).\n *\n * Adding a kind: append it here (grouped by category for readability),\n * add its `SCORE_RULES` entry in `./rules.ts`, and bump the package\n * minor. Removing a kind is a breaking change for stored history rows\n * that reference it — bump major and keep a label fallback.\n *\n * @module community-schema/social-score/kinds\n */\n\nimport { z } from 'zod';\n\nexport const SCORE_KIND_VALUES = [\n // Profile\n 'udp_approved',\n // Civic contribution\n 'crowdfunding_donation_verified',\n 'crowdfunding_campaign_succeeded',\n 'item_donated',\n 'lost_found_resolved',\n 'suggestions_complaints_submitted',\n 'suggestions_complaints_resolved',\n 'resource_returned_well',\n // Civic — volunteer work helping OTHER users\n 'item_pickup_to_center',\n 'udp_initiated',\n 'udp_completed',\n 'udp_verified',\n // Engagement\n 'event_organized',\n 'poll_created',\n 'poll_voted',\n // Engagement — community content that cleared the upvote bar\n 'post_qualified',\n 'poll_qualified',\n 'crowdfunding_qualified',\n 'suggestions_complaints_qualified',\n 'comment_qualified',\n // Leadership\n 'reform_role_granted',\n 'leader_role_granted',\n 'leader_added_volunteer',\n 'leader_mentee_reached_milestone',\n 'official_community_run',\n] as const;\n\nexport type ScoreKind = (typeof SCORE_KIND_VALUES)[number];\n\nexport const scoreKindSchema = z.enum(SCORE_KIND_VALUES);\n"]}
|
|
1
|
+
{"version":3,"file":"kinds.js","sourceRoot":"","sources":["../../src/social-score/kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,UAAU;IACV,oBAAoB;IACpB,cAAc;IACd,qBAAqB;IACrB,gCAAgC;IAChC,iCAAiC;IACjC,cAAc;IACd,qBAAqB;IACrB,kCAAkC;IAClC,iCAAiC;IACjC,wBAAwB;IACxB,6CAA6C;IAC7C,uBAAuB;IACvB,eAAe;IACf,eAAe;IACf,cAAc;IACd,aAAa;IACb,iBAAiB;IACjB,cAAc;IACd,YAAY;IACZ,6DAA6D;IAC7D,gBAAgB;IAChB,gBAAgB;IAChB,wBAAwB;IACxB,kCAAkC;IAClC,mBAAmB;IACnB,aAAa;IACb,qBAAqB;IACrB,qBAAqB;IACrB,wBAAwB;IACxB,iCAAiC;IACjC,wBAAwB;CAChB,CAAC;AAIX,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC","sourcesContent":["/**\n * Social-score KINDS — the canonical enumeration of every action that\n * can emit points. Single source of truth shared by the backend\n * emitter + rule table and any frontend that reasons about a specific\n * kind (e.g. a \"New\" chip keyed on a kind, or the recent-activity row\n * shape on the breakdown page).\n *\n * Adding a kind: append it here (grouped by category for readability),\n * add its `SCORE_RULES` entry in `./rules.ts`, and bump the package\n * minor. Removing a kind is a breaking change for stored history rows\n * that reference it — bump major and keep a label fallback.\n *\n * @module community-schema/social-score/kinds\n */\n\nimport { z } from 'zod';\n\nexport const SCORE_KIND_VALUES = [\n // Profile\n 'udp_step_completed',\n 'udp_approved',\n // Civic contribution\n 'crowdfunding_donation_verified',\n 'crowdfunding_campaign_succeeded',\n 'item_donated',\n 'lost_found_resolved',\n 'suggestions_complaints_submitted',\n 'suggestions_complaints_resolved',\n 'resource_returned_well',\n // Civic — volunteer work helping OTHER users\n 'item_pickup_to_center',\n 'udp_initiated',\n 'udp_completed',\n 'udp_verified',\n // Engagement\n 'event_organized',\n 'poll_created',\n 'poll_voted',\n // Engagement — community content that cleared the upvote bar\n 'post_qualified',\n 'poll_qualified',\n 'crowdfunding_qualified',\n 'suggestions_complaints_qualified',\n 'comment_qualified',\n // Leadership\n 'reform_role_granted',\n 'leader_role_granted',\n 'leader_added_volunteer',\n 'leader_mentee_reached_milestone',\n 'official_community_run',\n] as const;\n\nexport type ScoreKind = (typeof SCORE_KIND_VALUES)[number];\n\nexport const scoreKindSchema = z.enum(SCORE_KIND_VALUES);\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/social-score/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAI7C,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/social-score/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAI7C,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,CA2RpD,CAAC;AASF,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAaD;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,EAAE,CAiBpD;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,EAAwB,CAAC"}
|
|
@@ -59,6 +59,21 @@ export const MENTEE_MILESTONE_SCORE = 500;
|
|
|
59
59
|
export const LEADER_ELIGIBILITY_SCORE = 1000;
|
|
60
60
|
export const SCORE_RULES = {
|
|
61
61
|
// ─── Profile ──────────────────────────────────────────────────────────
|
|
62
|
+
// Progressive: awarded to the profile OWNER each time they complete a
|
|
63
|
+
// main UDP step, so the score climbs steadily as their completion %
|
|
64
|
+
// rises (not just at the end). `once-per-source` keyed on a per-step
|
|
65
|
+
// idempotency id so each of the ~12 steps awards exactly once and a
|
|
66
|
+
// re-save never re-awards. The bigger `udp_approved` bonus below lands
|
|
67
|
+
// separately when the profile is verified ("the rest of the points").
|
|
68
|
+
udp_step_completed: {
|
|
69
|
+
category: 'profile',
|
|
70
|
+
points: 5,
|
|
71
|
+
cap: { kind: 'once-per-source' },
|
|
72
|
+
verifierGated: false,
|
|
73
|
+
label: 'Completed a profile section',
|
|
74
|
+
catalogKey: 'ruleProfileSection',
|
|
75
|
+
isNew: true,
|
|
76
|
+
},
|
|
62
77
|
udp_approved: {
|
|
63
78
|
category: 'profile',
|
|
64
79
|
points: 100,
|
|
@@ -203,8 +218,12 @@ export const SCORE_RULES = {
|
|
|
203
218
|
},
|
|
204
219
|
poll_voted: {
|
|
205
220
|
category: 'engagement',
|
|
206
|
-
points:
|
|
207
|
-
cap
|
|
221
|
+
points: 10,
|
|
222
|
+
// No cap — democratic participation is unbounded; a member may vote
|
|
223
|
+
// on as many polls as they like. Farming a SINGLE poll is impossible
|
|
224
|
+
// anyway: the award is keyed on the vote-instance id and an unvote
|
|
225
|
+
// reverses it, so vote/unvote/re-vote always nets to "10 while
|
|
226
|
+
// currently voted, 0 otherwise".
|
|
208
227
|
verifierGated: false,
|
|
209
228
|
label: 'Poll vote',
|
|
210
229
|
catalogKey: 'rulePollVoted',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/social-score/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAgC7C,MAAM,CAAC,MAAM,WAAW,GAAiC;IACvD,yEAAyE;IACzE,YAAY,EAAE;QACZ,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACrB,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,kBAAkB;QACzB,UAAU,EAAE,qBAAqB;KAClC;IAED,yEAAyE;IACzE,8BAA8B,EAAE;QAC9B,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE;QAC/B,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,mBAAmB;QAC1B,UAAU,EAAE,sBAAsB;KACnC;IACD,+BAA+B,EAAE;QAC/B,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,oBAAoB;QAC3B,UAAU,EAAE,yBAAyB;KACtC;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,OAAO;QACjB,kEAAkE;QAClE,8DAA8D;QAC9D,iEAAiE;QACjE,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9B,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,YAAY;KACzB;IACD,mBAAmB,EAAE;QACnB,QAAQ,EAAE,OAAO;QACjB,4DAA4D;QAC5D,gEAAgE;QAChE,gEAAgE;QAChE,6DAA6D;QAC7D,2DAA2D;QAC3D,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9B,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,uBAAuB;QAC9B,UAAU,EAAE,uBAAuB;KACpC;IACD,gCAAgC,EAAE;QAChC,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9B,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,iCAAiC;QACxC,UAAU,EAAE,kBAAkB;KAC/B;IACD,+BAA+B,EAAE;QAC/B,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,mCAAmC;QAC1C,UAAU,EAAE,oBAAoB;KACjC;IACD,uEAAuE;IACvE,qEAAqE;IACrE,sBAAsB,EAAE;QACtB,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9B,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,+BAA+B;QACtC,UAAU,EAAE,oBAAoB;KACjC;IAED,yEAAyE;IACzE,sEAAsE;IACtE,6DAA6D;IAC7D,mEAAmE;IACnE,qEAAqE;IACrE,sBAAsB;IACtB,qBAAqB,EAAE;QACrB,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,oCAAoC;QAC3C,UAAU,EAAE,aAAa;KAC1B;IACD,+DAA+D;IAC/D,mEAAmE;IACnE,2DAA2D;IAC3D,aAAa,EAAE;QACb,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,+BAA+B;QACtC,UAAU,EAAE,cAAc;KAC3B;IACD,iEAAiE;IACjE,sEAAsE;IACtE,aAAa,EAAE;QACb,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,iCAAiC;QACxC,UAAU,EAAE,eAAe;KAC5B;IACD,qEAAqE;IACrE,mEAAmE;IACnE,wDAAwD;IACxD,YAAY,EAAE;QACZ,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,oBAAoB;QAC3B,UAAU,EAAE,eAAe;KAC5B;IAED,yEAAyE;IACzE,eAAe,EAAE;QACf,QAAQ,EAAE,YAAY;QACtB,6DAA6D;QAC7D,iEAAiE;QACjE,+DAA+D;QAC/D,iEAAiE;QACjE,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9B,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,iBAAiB;QACxB,UAAU,EAAE,oBAAoB;KACjC;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9B,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,iBAAiB;KAC9B;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE;QAC/B,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,WAAW;QAClB,UAAU,EAAE,eAAe;KAC5B;IAED,yEAAyE;IACzE,gEAAgE;IAChE,oEAAoE;IACpE,kEAAkE;IAClE,oEAAoE;IACpE,gEAAgE;IAChE,cAAc,EAAE;QACd,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,2BAA2B;QAClC,UAAU,EAAE,gBAAgB;KAC7B;IACD,cAAc,EAAE;QACd,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,2BAA2B;QAClC,UAAU,EAAE,gBAAgB;KAC7B;IACD,sBAAsB,EAAE;QACtB,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,iCAAiC;QACxC,UAAU,EAAE,sBAAsB;KACnC;IACD,gCAAgC,EAAE;QAChC,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,2CAA2C;QAClD,UAAU,EAAE,sBAAsB;KACnC;IACD,uEAAuE;IACvE,qEAAqE;IACrE,oBAAoB;IACpB,iBAAiB,EAAE;QACjB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,8BAA8B;QACrC,UAAU,EAAE,mBAAmB;QAC/B,KAAK,EAAE,IAAI;KACZ;IAED,yEAAyE;IACzE,iEAAiE;IACjE,4DAA4D;IAC5D,kEAAkE;IAClE,+DAA+D;IAC/D,6DAA6D;IAC7D,mBAAmB,EAAE;QACnB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,wBAAwB;QAC/B,UAAU,EAAE,eAAe;KAC5B;IACD,kEAAkE;IAClE,oEAAoE;IACpE,+DAA+D;IAC/D,qDAAqD;IACrD,mBAAmB,EAAE;QACnB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,qBAAqB;QAC5B,UAAU,EAAE,YAAY;KACzB;IACD,qEAAqE;IACrE,qEAAqE;IACrE,kCAAkC;IAClC,sBAAsB,EAAE;QACtB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,uBAAuB;QAC9B,UAAU,EAAE,aAAa;KAC1B;IACD,gEAAgE;IAChE,oEAAoE;IACpE,mEAAmE;IACnE,iDAAiD;IACjD,+BAA+B,EAAE;QAC/B,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,6BAA6B;QACpC,UAAU,EAAE,YAAY;KACzB;IACD,sBAAsB,EAAE;QACtB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAChC,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,oBAAoB;QAC3B,UAAU,EAAE,uBAAuB;KACpC;CACF,CAAC;AAyBF,qFAAqF;AACrF,MAAM,mBAAmB,GAAoB,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAE9F,2CAA2C;AAC3C,MAAM,kBAAkB,GAAkC;IACxD,OAAO,EAAE,YAAY;IACrB,UAAU,EAAE,cAAc;IAC1B,KAAK,EAAE,UAAU;IACjB,UAAU,EAAE,eAAe;CAC5B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,KAAK,MAAM,QAAQ,IAAI,mBAAmB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAmB,EAAE,CAAC;QACjC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YAC9C,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,aAAa;gBAAE,SAAS;YAC/D,KAAK,CAAC,IAAI,CAAC;gBACT,QAAQ,EAAE,IAAI,CAAC,UAAU;gBACzB,MAAM,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE;gBACzB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACvC,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAqB,iBAAiB,EAAE,CAAC","sourcesContent":["/**\n * Social-score RULES — the single source of truth for every point\n * award across the platform.\n *\n * Why this lives in the schema package\n * ────────────────────────────────────\n * The backend emitter reads `SCORE_RULES[kind]` for the weight,\n * category and cap; it NEVER hardcodes a point value. The frontend\n * \"How you earn points\" reference renders from `SCORE_RULE_CATALOG`.\n * Shipping both from one package means a weight change and the user-\n * facing guidance can't drift across a deploy — bump the package, both\n * consumers pick up the new values on `npm install`.\n *\n * What's in a rule\n * ────────────────\n * category — which breakdown bucket the points roll into.\n * points — base award per emission.\n * cap — optional anti-gaming bound:\n * • `once` — first emission per (user, kind).\n * • `once-per-source` — at most once per (user, kind,\n * sourceRef).\n * • `daily, max: N` — at most N awarding emissions per\n * UTC day; excess still records an event (audit /\n * replay) but awards 0.\n * • undefined → no cap.\n * verifierGated — when true the action MUST be independently verified\n * before emitting (belt-and-braces guard at the rule\n * level so a wiring mistake can't leak points).\n *\n * @module community-schema/social-score/rules\n */\n\nimport type { ScoreCategory } from './categories.js';\nimport type { ScoreKind } from './kinds.js';\n\n// ─── Anti-gaming constants ─────────────────────────────────────────────────\n\n/**\n * Net-upvote bar a piece of community content must clear before its\n * author is awarded a one-time \"qualified\" point bundle. The vote\n * service maintains a denormalised signed `score` (upvotes − downvotes)\n * on every content doc; the engagement hook fires the award the first\n * time that `score` crosses this threshold upward.\n */\nexport const QUALIFYING_UPVOTES = 20;\n\n/**\n * Lower net-upvote bar for COMMENTS — a comment is a smaller unit of\n * contribution than a top-level post, so it qualifies its author at a\n * gentler threshold. Separate constant so the two bars move\n * independently.\n */\nexport const QUALIFYING_UPVOTES_COMMENT = 5;\n\n/**\n * Score a recruited volunteer must reach for the leader who added them\n * to earn the mentoring milestone bonus.\n */\nexport const MENTEE_MILESTONE_SCORE = 500;\n\n/**\n * Minimum total social score an applicant must hold to QUALIFY for a\n * leader role on merit. A manager/superadmin can still approve a\n * sub-threshold applicant (discretionary override) — this gate only\n * blocks a *leader* approving a peer who hasn't earned it.\n */\nexport const LEADER_ELIGIBILITY_SCORE = 1000;\n\n// ─── Rule shape ─────────────────────────────────────────────────────────────\n\nexport type ScoreCap =\n | { kind: 'once' }\n | { kind: 'once-per-source' }\n | { kind: 'daily'; max: number };\n\nexport interface ScoreRule {\n category: ScoreCategory;\n points: number;\n cap?: ScoreCap;\n verifierGated: boolean;\n /** Human-facing fallback label rendered on the breakdown history. */\n label: string;\n /**\n * i18n key (socialScore namespace) for the \"How you earn points\"\n * catalog row. Every awarding rule carries one so the catalog is\n * DERIVED from this table (see `buildScoreCatalog`) and can never\n * drift out of sync with what actually earns points.\n */\n catalogKey: string;\n /** Show a \"New\" chip on the catalog row. */\n isNew?: boolean;\n /**\n * Exclude this rule from the user-facing catalog while STILL awarding\n * points (e.g. an internal/automatic award). Default: shown.\n */\n catalogHidden?: boolean;\n}\n\nexport const SCORE_RULES: Record<ScoreKind, ScoreRule> = {\n // ─── Profile ──────────────────────────────────────────────────────────\n udp_approved: {\n category: 'profile',\n points: 100,\n cap: { kind: 'once' },\n verifierGated: true,\n label: 'Profile verified',\n catalogKey: 'ruleProfileComplete',\n },\n\n // ─── Civic contribution ───────────────────────────────────────────────\n crowdfunding_donation_verified: {\n category: 'civic',\n points: 25,\n cap: { kind: 'daily', max: 10 },\n verifierGated: true,\n label: 'Verified donation',\n catalogKey: 'ruleDonateFundraiser',\n },\n crowdfunding_campaign_succeeded: {\n category: 'civic',\n points: 100,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Campaign succeeded',\n catalogKey: 'ruleFundraiserSucceeded',\n },\n item_donated: {\n category: 'civic',\n // Awarded to the DONOR when their item completes the journey to a\n // recipient (terminal DONATED state). The social worth is the\n // completed handover, not upvotes — so this is NOT upvote-gated.\n points: 10,\n cap: { kind: 'daily', max: 5 },\n verifierGated: true,\n label: 'Item donated',\n catalogKey: 'ruleDonate',\n },\n lost_found_resolved: {\n category: 'civic',\n // Resolution is creator-driven (the report owner approves a\n // claim) — no third-party signal. The daily cap throttles mass-\n // create + self-approve farming via a colluding \"claimant\". The\n // unique idempotency index on (userId, kind, sourceId) still\n // enforces once-per-report (no double-counting on toggle).\n points: 50,\n cap: { kind: 'daily', max: 3 },\n verifierGated: false,\n label: 'Lost & found resolved',\n catalogKey: 'ruleLostFoundResolved',\n },\n suggestions_complaints_submitted: {\n category: 'civic',\n points: 10,\n cap: { kind: 'daily', max: 3 },\n verifierGated: false,\n label: 'Suggestions & complaints report',\n catalogKey: 'ruleSubmitReport',\n },\n suggestions_complaints_resolved: {\n category: 'civic',\n points: 75,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Suggestions & complaints resolved',\n catalogKey: 'ruleReportResolved',\n },\n // A lender rated a returned item well (overall >= 4★). Daily-capped to\n // throttle farming; idempotent per borrow-request via the source id.\n resource_returned_well: {\n category: 'civic',\n points: 15,\n cap: { kind: 'daily', max: 5 },\n verifierGated: false,\n label: 'Returned a borrowed item well',\n catalogKey: 'ruleReturnResource',\n },\n\n // ─── Civic — volunteer work helping OTHER users ───────────────────────\n // Credited to the VOLUNTEER (the actor), not the donation owner, when\n // they physically deliver a collected donation to the centre\n // (AT_CENTER transition). Gated by that verified transition, so no\n // separate verifierGated flag is needed; once-per-source = one award\n // per donation moved.\n item_pickup_to_center: {\n category: 'civic',\n points: 50,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Delivered a donation to the centre',\n catalogKey: 'ruleDeliver',\n },\n // Credited to the VOLUNTEER who starts someone else's detailed\n // profile in survey/fill mode. once-per-source keyed on the target\n // profile so re-opening the same profile doesn't re-award.\n udp_initiated: {\n category: 'civic',\n points: 10,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Started a profile for someone',\n catalogKey: 'ruleUdpStart',\n },\n // Credited to the VOLUNTEER who submits a completed profile they\n // filled for someone else (PENDING_APPROVAL transition in fill mode).\n udp_completed: {\n category: 'civic',\n points: 20,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Completed a profile for someone',\n catalogKey: 'ruleUdpFinish',\n },\n // Credited to the VERIFIER (leader/official) who approves a detailed\n // profile. Distinct from `udp_approved`, which credits the profile\n // OWNER. once-per-source keyed on the approved profile.\n udp_verified: {\n category: 'civic',\n points: 10,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Verified a profile',\n catalogKey: 'ruleUdpVerify',\n },\n\n // ─── Engagement ───────────────────────────────────────────────────────\n event_organized: {\n category: 'engagement',\n // Awarded at event creation (no attendance signal yet — that\n // would need an event-completion flow). Daily cap of 2 throttles\n // mass-create farming; per-source uniqueness still enforced by\n // the idempotency index so editing an event isn't a second emit.\n points: 50,\n cap: { kind: 'daily', max: 2 },\n verifierGated: false,\n label: 'Event organized',\n catalogKey: 'ruleEventOrganized',\n },\n poll_created: {\n category: 'engagement',\n points: 10,\n cap: { kind: 'daily', max: 3 },\n verifierGated: false,\n label: 'Poll created',\n catalogKey: 'rulePollCreated',\n },\n poll_voted: {\n category: 'engagement',\n points: 2,\n cap: { kind: 'daily', max: 10 },\n verifierGated: false,\n label: 'Poll vote',\n catalogKey: 'rulePollVoted',\n },\n\n // ─── Engagement — content the community upvoted past the bar ──────────\n // These fire ONCE per content item, the first time its net vote\n // `score` crosses QUALIFYING_UPVOTES upward (see engagement.service\n // setVote hook). Awarded to the content's author. Anti-farming is\n // structural: you can't earn by self-posting — the community has to\n // actually upvote you. once-per-source keyed on the content id.\n post_qualified: {\n category: 'engagement',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Post the community backed',\n catalogKey: 'rulePostBacked',\n },\n poll_qualified: {\n category: 'engagement',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Poll the community backed',\n catalogKey: 'rulePollBacked',\n },\n crowdfunding_qualified: {\n category: 'civic',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Fundraiser the community backed',\n catalogKey: 'ruleFundraiserBacked',\n },\n suggestions_complaints_qualified: {\n category: 'civic',\n points: 7,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Suggestion/complaint the community backed',\n catalogKey: 'ruleSuggestionBacked',\n },\n // A comment the community upvoted past QUALIFYING_UPVOTES_COMMENT (5).\n // Smaller unit than a post, lower bar, awarded to the comment author\n // once per comment.\n comment_qualified: {\n category: 'engagement',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Comment the community backed',\n catalogKey: 'ruleCommentBacked',\n isNew: true,\n },\n\n // ─── Leadership ───────────────────────────────────────────────────────\n // Fires when a user's reform role is elevated through any of the\n // application flows (volunteer-application approval, leader\n // promotion). The label stays generic so the breakdown page reads\n // sensibly regardless of which level was actually granted; the\n // metadata field on the event row carries the specific role.\n reform_role_granted: {\n category: 'leadership',\n points: 200,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Volunteer role granted',\n catalogKey: 'ruleVolunteer',\n },\n // Promotion to a LEADER role. Distinct from `reform_role_granted`\n // (volunteer, 200) so the two milestones carry their own weight and\n // breakdown line. Eligibility (>= LEADER_ELIGIBILITY_SCORE) is\n // enforced in the leader-application flow, not here.\n leader_role_granted: {\n category: 'leadership',\n points: 500,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Leader role granted',\n catalogKey: 'ruleLeader',\n },\n // Credited to the LEADER each time a new volunteer is added in their\n // area. once-per-source keyed on the recruited volunteer's user id —\n // one award per person recruited.\n leader_added_volunteer: {\n category: 'leadership',\n points: 10,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Recruited a volunteer',\n catalogKey: 'ruleRecruit',\n },\n // Credited to the recruiting LEADER when a volunteer they added\n // reaches MENTEE_MILESTONE_SCORE. Fired by the cross-user milestone\n // check in the emitter. once-per-source keyed on the mentee's user\n // id — one bonus per mentee who crosses the bar.\n leader_mentee_reached_milestone: {\n category: 'leadership',\n points: 100,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Mentored a rising volunteer',\n catalogKey: 'ruleMentee',\n },\n official_community_run: {\n category: 'leadership',\n points: 100,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Official community',\n catalogKey: 'ruleOfficialCommunity',\n },\n};\n\n// ─── Display catalog (\"How you earn points\") ────────────────────────────────\n// DERIVED from SCORE_RULES (see `buildScoreCatalog`) so the user-facing\n// \"How you earn points\" surface lists EVERY rule that actually awards\n// points — it can never silently omit an earning path. Labels are i18n\n// KEYS (resolved in the `socialScore` namespace); point figures are\n// numbers-as-data derived straight from each rule's `points`.\n\nexport interface ScoreRuleRef {\n /** i18n key (socialScore namespace) for the action description. */\n labelKey: string;\n /** Display reward derived from the rule, e.g. \"+5\". */\n points: string;\n /** Marks a freshly-added rule with a \"New\" chip. */\n isNew?: boolean;\n}\n\nexport interface ScoreRuleGroup {\n category: ScoreCategory;\n /** i18n key (socialScore namespace) for the section heading. */\n groupKey: string;\n rules: ScoreRuleRef[];\n}\n\n/** Display order of the catalog sections (Profile, Community, Civic, Leadership). */\nconst CATALOG_GROUP_ORDER: ScoreCategory[] = ['profile', 'engagement', 'civic', 'leadership'];\n\n/** Category → section-heading i18n key. */\nconst CATEGORY_GROUP_KEY: Record<ScoreCategory, string> = {\n profile: 'grpProfile',\n engagement: 'grpCommunity',\n civic: 'grpCivic',\n leadership: 'grpLeadership',\n};\n\n/**\n * Build the \"How you earn points\" catalog by projecting SCORE_RULES into\n * display sections. Every non-hidden rule becomes a row under its\n * category, so the catalog is guaranteed to stay in lockstep with the\n * award table — add a rule and it appears automatically.\n */\nexport function buildScoreCatalog(): ScoreRuleGroup[] {\n const groups: ScoreRuleGroup[] = [];\n for (const category of CATALOG_GROUP_ORDER) {\n const rules: ScoreRuleRef[] = [];\n for (const rule of Object.values(SCORE_RULES)) {\n if (rule.category !== category || rule.catalogHidden) continue;\n rules.push({\n labelKey: rule.catalogKey,\n points: `+${rule.points}`,\n ...(rule.isNew ? { isNew: true } : {}),\n });\n }\n if (rules.length > 0) {\n groups.push({ category, groupKey: CATEGORY_GROUP_KEY[category], rules });\n }\n }\n return groups;\n}\n\nexport const SCORE_RULE_CATALOG: ScoreRuleGroup[] = buildScoreCatalog();\n"]}
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/social-score/rules.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Social-score RULES — the single source of truth for every point\n * award across the platform.\n *\n * Why this lives in the schema package\n * ────────────────────────────────────\n * The backend emitter reads `SCORE_RULES[kind]` for the weight,\n * category and cap; it NEVER hardcodes a point value. The frontend\n * \"How you earn points\" reference renders from `SCORE_RULE_CATALOG`.\n * Shipping both from one package means a weight change and the user-\n * facing guidance can't drift across a deploy — bump the package, both\n * consumers pick up the new values on `npm install`.\n *\n * What's in a rule\n * ────────────────\n * category — which breakdown bucket the points roll into.\n * points — base award per emission.\n * cap — optional anti-gaming bound:\n * • `once` — first emission per (user, kind).\n * • `once-per-source` — at most once per (user, kind,\n * sourceRef).\n * • `daily, max: N` — at most N awarding emissions per\n * UTC day; excess still records an event (audit /\n * replay) but awards 0.\n * • undefined → no cap.\n * verifierGated — when true the action MUST be independently verified\n * before emitting (belt-and-braces guard at the rule\n * level so a wiring mistake can't leak points).\n *\n * @module community-schema/social-score/rules\n */\n\nimport type { ScoreCategory } from './categories.js';\nimport type { ScoreKind } from './kinds.js';\n\n// ─── Anti-gaming constants ─────────────────────────────────────────────────\n\n/**\n * Net-upvote bar a piece of community content must clear before its\n * author is awarded a one-time \"qualified\" point bundle. The vote\n * service maintains a denormalised signed `score` (upvotes − downvotes)\n * on every content doc; the engagement hook fires the award the first\n * time that `score` crosses this threshold upward.\n */\nexport const QUALIFYING_UPVOTES = 20;\n\n/**\n * Lower net-upvote bar for COMMENTS — a comment is a smaller unit of\n * contribution than a top-level post, so it qualifies its author at a\n * gentler threshold. Separate constant so the two bars move\n * independently.\n */\nexport const QUALIFYING_UPVOTES_COMMENT = 5;\n\n/**\n * Score a recruited volunteer must reach for the leader who added them\n * to earn the mentoring milestone bonus.\n */\nexport const MENTEE_MILESTONE_SCORE = 500;\n\n/**\n * Minimum total social score an applicant must hold to QUALIFY for a\n * leader role on merit. A manager/superadmin can still approve a\n * sub-threshold applicant (discretionary override) — this gate only\n * blocks a *leader* approving a peer who hasn't earned it.\n */\nexport const LEADER_ELIGIBILITY_SCORE = 1000;\n\n// ─── Rule shape ─────────────────────────────────────────────────────────────\n\nexport type ScoreCap =\n | { kind: 'once' }\n | { kind: 'once-per-source' }\n | { kind: 'daily'; max: number };\n\nexport interface ScoreRule {\n category: ScoreCategory;\n points: number;\n cap?: ScoreCap;\n verifierGated: boolean;\n /** Human-facing fallback label rendered on the breakdown history. */\n label: string;\n /**\n * i18n key (socialScore namespace) for the \"How you earn points\"\n * catalog row. Every awarding rule carries one so the catalog is\n * DERIVED from this table (see `buildScoreCatalog`) and can never\n * drift out of sync with what actually earns points.\n */\n catalogKey: string;\n /** Show a \"New\" chip on the catalog row. */\n isNew?: boolean;\n /**\n * Exclude this rule from the user-facing catalog while STILL awarding\n * points (e.g. an internal/automatic award). Default: shown.\n */\n catalogHidden?: boolean;\n}\n\nexport const SCORE_RULES: Record<ScoreKind, ScoreRule> = {\n // ─── Profile ──────────────────────────────────────────────────────────\n // Progressive: awarded to the profile OWNER each time they complete a\n // main UDP step, so the score climbs steadily as their completion %\n // rises (not just at the end). `once-per-source` keyed on a per-step\n // idempotency id so each of the ~12 steps awards exactly once and a\n // re-save never re-awards. The bigger `udp_approved` bonus below lands\n // separately when the profile is verified (\"the rest of the points\").\n udp_step_completed: {\n category: 'profile',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Completed a profile section',\n catalogKey: 'ruleProfileSection',\n isNew: true,\n },\n udp_approved: {\n category: 'profile',\n points: 100,\n cap: { kind: 'once' },\n verifierGated: true,\n label: 'Profile verified',\n catalogKey: 'ruleProfileComplete',\n },\n\n // ─── Civic contribution ───────────────────────────────────────────────\n crowdfunding_donation_verified: {\n category: 'civic',\n points: 25,\n cap: { kind: 'daily', max: 10 },\n verifierGated: true,\n label: 'Verified donation',\n catalogKey: 'ruleDonateFundraiser',\n },\n crowdfunding_campaign_succeeded: {\n category: 'civic',\n points: 100,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Campaign succeeded',\n catalogKey: 'ruleFundraiserSucceeded',\n },\n item_donated: {\n category: 'civic',\n // Awarded to the DONOR when their item completes the journey to a\n // recipient (terminal DONATED state). The social worth is the\n // completed handover, not upvotes — so this is NOT upvote-gated.\n points: 10,\n cap: { kind: 'daily', max: 5 },\n verifierGated: true,\n label: 'Item donated',\n catalogKey: 'ruleDonate',\n },\n lost_found_resolved: {\n category: 'civic',\n // Resolution is creator-driven (the report owner approves a\n // claim) — no third-party signal. The daily cap throttles mass-\n // create + self-approve farming via a colluding \"claimant\". The\n // unique idempotency index on (userId, kind, sourceId) still\n // enforces once-per-report (no double-counting on toggle).\n points: 50,\n cap: { kind: 'daily', max: 3 },\n verifierGated: false,\n label: 'Lost & found resolved',\n catalogKey: 'ruleLostFoundResolved',\n },\n suggestions_complaints_submitted: {\n category: 'civic',\n points: 10,\n cap: { kind: 'daily', max: 3 },\n verifierGated: false,\n label: 'Suggestions & complaints report',\n catalogKey: 'ruleSubmitReport',\n },\n suggestions_complaints_resolved: {\n category: 'civic',\n points: 75,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Suggestions & complaints resolved',\n catalogKey: 'ruleReportResolved',\n },\n // A lender rated a returned item well (overall >= 4★). Daily-capped to\n // throttle farming; idempotent per borrow-request via the source id.\n resource_returned_well: {\n category: 'civic',\n points: 15,\n cap: { kind: 'daily', max: 5 },\n verifierGated: false,\n label: 'Returned a borrowed item well',\n catalogKey: 'ruleReturnResource',\n },\n\n // ─── Civic — volunteer work helping OTHER users ───────────────────────\n // Credited to the VOLUNTEER (the actor), not the donation owner, when\n // they physically deliver a collected donation to the centre\n // (AT_CENTER transition). Gated by that verified transition, so no\n // separate verifierGated flag is needed; once-per-source = one award\n // per donation moved.\n item_pickup_to_center: {\n category: 'civic',\n points: 50,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Delivered a donation to the centre',\n catalogKey: 'ruleDeliver',\n },\n // Credited to the VOLUNTEER who starts someone else's detailed\n // profile in survey/fill mode. once-per-source keyed on the target\n // profile so re-opening the same profile doesn't re-award.\n udp_initiated: {\n category: 'civic',\n points: 10,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Started a profile for someone',\n catalogKey: 'ruleUdpStart',\n },\n // Credited to the VOLUNTEER who submits a completed profile they\n // filled for someone else (PENDING_APPROVAL transition in fill mode).\n udp_completed: {\n category: 'civic',\n points: 20,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Completed a profile for someone',\n catalogKey: 'ruleUdpFinish',\n },\n // Credited to the VERIFIER (leader/official) who approves a detailed\n // profile. Distinct from `udp_approved`, which credits the profile\n // OWNER. once-per-source keyed on the approved profile.\n udp_verified: {\n category: 'civic',\n points: 10,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Verified a profile',\n catalogKey: 'ruleUdpVerify',\n },\n\n // ─── Engagement ───────────────────────────────────────────────────────\n event_organized: {\n category: 'engagement',\n // Awarded at event creation (no attendance signal yet — that\n // would need an event-completion flow). Daily cap of 2 throttles\n // mass-create farming; per-source uniqueness still enforced by\n // the idempotency index so editing an event isn't a second emit.\n points: 50,\n cap: { kind: 'daily', max: 2 },\n verifierGated: false,\n label: 'Event organized',\n catalogKey: 'ruleEventOrganized',\n },\n poll_created: {\n category: 'engagement',\n points: 10,\n cap: { kind: 'daily', max: 3 },\n verifierGated: false,\n label: 'Poll created',\n catalogKey: 'rulePollCreated',\n },\n poll_voted: {\n category: 'engagement',\n points: 10,\n // No cap — democratic participation is unbounded; a member may vote\n // on as many polls as they like. Farming a SINGLE poll is impossible\n // anyway: the award is keyed on the vote-instance id and an unvote\n // reverses it, so vote/unvote/re-vote always nets to \"10 while\n // currently voted, 0 otherwise\".\n verifierGated: false,\n label: 'Poll vote',\n catalogKey: 'rulePollVoted',\n },\n\n // ─── Engagement — content the community upvoted past the bar ──────────\n // These fire ONCE per content item, the first time its net vote\n // `score` crosses QUALIFYING_UPVOTES upward (see engagement.service\n // setVote hook). Awarded to the content's author. Anti-farming is\n // structural: you can't earn by self-posting — the community has to\n // actually upvote you. once-per-source keyed on the content id.\n post_qualified: {\n category: 'engagement',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Post the community backed',\n catalogKey: 'rulePostBacked',\n },\n poll_qualified: {\n category: 'engagement',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Poll the community backed',\n catalogKey: 'rulePollBacked',\n },\n crowdfunding_qualified: {\n category: 'civic',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Fundraiser the community backed',\n catalogKey: 'ruleFundraiserBacked',\n },\n suggestions_complaints_qualified: {\n category: 'civic',\n points: 7,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Suggestion/complaint the community backed',\n catalogKey: 'ruleSuggestionBacked',\n },\n // A comment the community upvoted past QUALIFYING_UPVOTES_COMMENT (5).\n // Smaller unit than a post, lower bar, awarded to the comment author\n // once per comment.\n comment_qualified: {\n category: 'engagement',\n points: 5,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Comment the community backed',\n catalogKey: 'ruleCommentBacked',\n isNew: true,\n },\n\n // ─── Leadership ───────────────────────────────────────────────────────\n // Fires when a user's reform role is elevated through any of the\n // application flows (volunteer-application approval, leader\n // promotion). The label stays generic so the breakdown page reads\n // sensibly regardless of which level was actually granted; the\n // metadata field on the event row carries the specific role.\n reform_role_granted: {\n category: 'leadership',\n points: 200,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Volunteer role granted',\n catalogKey: 'ruleVolunteer',\n },\n // Promotion to a LEADER role. Distinct from `reform_role_granted`\n // (volunteer, 200) so the two milestones carry their own weight and\n // breakdown line. Eligibility (>= LEADER_ELIGIBILITY_SCORE) is\n // enforced in the leader-application flow, not here.\n leader_role_granted: {\n category: 'leadership',\n points: 500,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Leader role granted',\n catalogKey: 'ruleLeader',\n },\n // Credited to the LEADER each time a new volunteer is added in their\n // area. once-per-source keyed on the recruited volunteer's user id —\n // one award per person recruited.\n leader_added_volunteer: {\n category: 'leadership',\n points: 10,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Recruited a volunteer',\n catalogKey: 'ruleRecruit',\n },\n // Credited to the recruiting LEADER when a volunteer they added\n // reaches MENTEE_MILESTONE_SCORE. Fired by the cross-user milestone\n // check in the emitter. once-per-source keyed on the mentee's user\n // id — one bonus per mentee who crosses the bar.\n leader_mentee_reached_milestone: {\n category: 'leadership',\n points: 100,\n cap: { kind: 'once-per-source' },\n verifierGated: false,\n label: 'Mentored a rising volunteer',\n catalogKey: 'ruleMentee',\n },\n official_community_run: {\n category: 'leadership',\n points: 100,\n cap: { kind: 'once-per-source' },\n verifierGated: true,\n label: 'Official community',\n catalogKey: 'ruleOfficialCommunity',\n },\n};\n\n// ─── Display catalog (\"How you earn points\") ────────────────────────────────\n// DERIVED from SCORE_RULES (see `buildScoreCatalog`) so the user-facing\n// \"How you earn points\" surface lists EVERY rule that actually awards\n// points — it can never silently omit an earning path. Labels are i18n\n// KEYS (resolved in the `socialScore` namespace); point figures are\n// numbers-as-data derived straight from each rule's `points`.\n\nexport interface ScoreRuleRef {\n /** i18n key (socialScore namespace) for the action description. */\n labelKey: string;\n /** Display reward derived from the rule, e.g. \"+5\". */\n points: string;\n /** Marks a freshly-added rule with a \"New\" chip. */\n isNew?: boolean;\n}\n\nexport interface ScoreRuleGroup {\n category: ScoreCategory;\n /** i18n key (socialScore namespace) for the section heading. */\n groupKey: string;\n rules: ScoreRuleRef[];\n}\n\n/** Display order of the catalog sections (Profile, Community, Civic, Leadership). */\nconst CATALOG_GROUP_ORDER: ScoreCategory[] = ['profile', 'engagement', 'civic', 'leadership'];\n\n/** Category → section-heading i18n key. */\nconst CATEGORY_GROUP_KEY: Record<ScoreCategory, string> = {\n profile: 'grpProfile',\n engagement: 'grpCommunity',\n civic: 'grpCivic',\n leadership: 'grpLeadership',\n};\n\n/**\n * Build the \"How you earn points\" catalog by projecting SCORE_RULES into\n * display sections. Every non-hidden rule becomes a row under its\n * category, so the catalog is guaranteed to stay in lockstep with the\n * award table — add a rule and it appears automatically.\n */\nexport function buildScoreCatalog(): ScoreRuleGroup[] {\n const groups: ScoreRuleGroup[] = [];\n for (const category of CATALOG_GROUP_ORDER) {\n const rules: ScoreRuleRef[] = [];\n for (const rule of Object.values(SCORE_RULES)) {\n if (rule.category !== category || rule.catalogHidden) continue;\n rules.push({\n labelKey: rule.catalogKey,\n points: `+${rule.points}`,\n ...(rule.isNew ? { isNew: true } : {}),\n });\n }\n if (rules.length > 0) {\n groups.push({ category, groupKey: CATEGORY_GROUP_KEY[category], rules });\n }\n }\n return groups;\n}\n\nexport const SCORE_RULE_CATALOG: ScoreRuleGroup[] = buildScoreCatalog();\n"]}
|
|
@@ -16,6 +16,7 @@ import { z } from 'zod';
|
|
|
16
16
|
/** One row of the recent-activity history on the breakdown surface. */
|
|
17
17
|
export declare const recentScoreEventSchema: z.ZodObject<{
|
|
18
18
|
kind: z.ZodEnum<{
|
|
19
|
+
udp_step_completed: "udp_step_completed";
|
|
19
20
|
udp_approved: "udp_approved";
|
|
20
21
|
crowdfunding_donation_verified: "crowdfunding_donation_verified";
|
|
21
22
|
crowdfunding_campaign_succeeded: "crowdfunding_campaign_succeeded";
|
|
@@ -76,6 +77,7 @@ export declare const scoreBreakdownSchema: z.ZodObject<{
|
|
|
76
77
|
tier: z.ZodString;
|
|
77
78
|
recent: z.ZodArray<z.ZodObject<{
|
|
78
79
|
kind: z.ZodEnum<{
|
|
80
|
+
udp_step_completed: "udp_step_completed";
|
|
79
81
|
udp_approved: "udp_approved";
|
|
80
82
|
crowdfunding_donation_verified: "crowdfunding_donation_verified";
|
|
81
83
|
crowdfunding_campaign_succeeded: "crowdfunding_campaign_succeeded";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/social-score/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,uEAAuE;AACvE,eAAO,MAAM,sBAAsB
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/social-score/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,uEAAuE;AACvE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAOjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,kEAAkE;AAClE,eAAO,MAAM,qBAAqB;;;;;iBAKhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAK/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,yEAAyE;AACzE,eAAO,MAAM,wBAAwB;;;;iBAInC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB;;;;;;;;iBAQjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,uDAAwD,CAAC;AAC9F,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AACzE,eAAO,MAAM,sBAAsB;;;;;EAAmC,CAAC;AAEvE,kDAAkD;AAClD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;iBAcpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jansathi-community-schema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"description": "Shared Zod schemas + TypeScript types for the Jansathi hyperlocal community feature (feed, posts, engagement, social graph, groups, communities).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|