Skip to content

Commit 408aad2

Browse files
committed
Add Pol roster_excluded policy layer and API enforcement
Store exclusion on Pol, filter GET /api/congress roster, block celebration and payment intent before Stripe when roster_excluded is true. Extend vest query for payment validation without changing watcher has_stakes jobs.
1 parent 0097c8d commit 408aad2

19 files changed

Lines changed: 178 additions & 24 deletions

.cursor/rules/00-index.mdc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ This directory contains comprehensive cursor rules for the POWERBACK project. Th
5252
- **21-election-dates.mdc** - Election dates handling, snapshots, fallbacks
5353
- **22-logging-telemetry.mdc** - Logging patterns, PII handling, central logger
5454
- **26-copy-registry.mdc** - Copy text registry patterns (referenced in specs)
55+
- **42-pol-roster-exclusion.mdc** - Policy exclusions from roster and new Celebrations (`roster_excluded` vs watcher `has_stakes`)
5556

5657
## How to Use These Rules
5758

@@ -65,6 +66,7 @@ This directory contains comprehensive cursor rules for the POWERBACK project. Th
6566
Some rules reference others:
6667
- `specs/frontend-ui.md` references `28-css-property-ordering.mdc` and `29-bootstrap-css-blending.mdc`
6768
- `specs/escrow-and-aggregate-limits.md` references `26-copy-registry.mdc`
69+
- `specs/pol-roster-exclusion.md` references `42-pol-roster-exclusion.mdc`
6870

6971
## Rule Maintenance
7072

.cursor/rules/05-api-patterns.mdc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,11 @@ interface PaymentValidationResponse {
335335
message: string;
336336
};
337337
}
338+
339+
// Roster exclusion (before Stripe): HTTP 400 body may be:
340+
// { code: 'POL_ROSTER_EXCLUDED', message: string } — not the shape above.
341+
342+
// GET /api/congress roster: has_stakes true and roster_excluded not true
338343
```
339344

340345
## Async/Await Pattern

.cursor/rules/12-models.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module.exports = mongoose.model('Model', ModelSchema);
4343
- `User.js` - User accounts and profiles
4444
- `Celebration.js` - Donation events
4545
- `Bill.js` - Congressional bills
46-
- `Pol.js` - Politicians and candidates
46+
- `Pol.js` - Politicians and candidates (includes `has_stakes` from watchers and policy `roster_excluded` / `roster_exclusion_*`; do not use `has_stakes` for policy exclusion — see `42-pol-roster-exclusion.mdc`)
4747
- `Applicant.js` - Users awaiting account verification
4848
- `ExUser.js` - Deleted users (prevent re-registration)
4949
- `Key.js` - API keys and credentials

.cursor/rules/18-ai-assistant-guidelines.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ priority: 1
7272
- Cookie security and route scoping
7373

7474
### Documentation Updates
75-
- **Update relevant docs**: When changing behavior, update all relevant docs (API, HOOKS, CONTEXTS, DONATION_LIMITS, ELECTION_DATE_NOTIFICATIONS)
75+
- **Update relevant docs**: When changing behavior, update all relevant docs (API, HOOKS, CONTEXTS, DONATION_LIMITS, ELECTION_DATE_NOTIFICATIONS, specs e.g. pol-roster-exclusion when roster policy changes)
7676
- **Timing**: Update docs at the "end" of a refactor (ask user if it's the right time)
7777
- **Link to docs**: Keep long process/flow explanations in docs/ and link from code comments
7878

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
description: Pol roster exclusion policy (separate from has_stakes watcher logic)
3+
globs:
4+
- 'models/Pol.js'
5+
- 'services/congress/polRosterEligibility.js'
6+
- 'controller/congress/pols.js'
7+
- 'controller/congress/vest.js'
8+
- 'services/celebration/orchestrationService.js'
9+
- 'routes/api/payments.js'
10+
alwaysApply: false
11+
priority: 3
12+
---
13+
14+
# Pol roster exclusion (policy layer)
15+
16+
## Separation from has_stakes
17+
18+
- `has_stakes` is computed by watcher jobs (e.g. `challengersWatcher`). Do not flip or overload it for policy exclusions (Speaker, left office, data holds, etc.).
19+
- Use `roster_excluded` and related fields on `Pol` for POWERBACK policy exclusions.
20+
21+
## Pol schema (see `models/Pol.js`)
22+
23+
- `roster_excluded` (Boolean, default false)
24+
- `roster_exclusion_reason` (String)
25+
- `roster_exclusion_category` (String; conventions listed in `ROSTER_EXCLUSION_CATEGORIES` in `services/congress/polRosterEligibility.js`)
26+
- `roster_exclusion_updated_at` (Date)
27+
28+
## Enforcement
29+
30+
- Selectable roster: `GET /api/congress` uses `controller/congress/pols.js` — query `has_stakes: true` and `roster_excluded: { $ne: true }`.
31+
- New celebrations: `services/celebration/orchestrationService.js` rejects excluded `pol_id` before DB write and side effects.
32+
- Payment intent: `routes/api/payments.js` rejects excluded `pol_id` before Stripe / `createPayment`.
33+
- `controller/congress/vest.js` requires both `has_stakes` and not `roster_excluded` for payment validation.
34+
35+
## User-facing errors
36+
37+
- HTTP 400 with `code: POL_ROSTER_EXCLUDED` and plain-language `message` from `polRosterEligibility.js`. Client surfaces via `usePaymentProcessing` rejection reasons.
38+
39+
## Specs
40+
41+
- `specs/pol-roster-exclusion.md`

docs/API.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,20 @@ Each category is a link to its [`Routes`](../routes/) or relevant folder.
4141
- [`resolveDonation`](../controller/celebrations/resolve.js) converts a Celebration into a donation by updating the document
4242
- [`sendReceipt`](../controller/celebrations/receipt.js) emails Celebration receipt to user (uses refactored email system)
4343
- [`getWhatPolsHaveInEscrow`](../controller/celebrations/find/params/escrowed.js) sums all donation amounts for each politician from across the userbase
44+
- **Roster exclusion**: HTTP `400` with `code: POL_ROSTER_EXCLUDED` when `pol_id` is a `Pol` with `roster_excluded: true` — enforced in [`orchestrationService`](../services/celebration/orchestrationService.js) before create. See [`specs/pol-roster-exclusion.md`](../specs/pol-roster-exclusion.md).
4445

4546
### **[`Congress`](../routes/api/congress.js)**
4647

47-
- [`getPolsByIds`](../controller/congress/methods/put/pols.js) returns a group of **Politicians** based on the ID(s) provided
48-
- [`getPol`](../controller/congress/methods/get/pol.js) returns a single **Politician's** document
49-
- [`getBill`](../controller/congress/methods/get/bill.js) returns a single **Bill's** document
48+
- `GET /api/congress/` — List politicians for the **selectable roster** (lobby carousel, search). [`getPols`](../controller/congress/pols.js): `has_stakes: true` and `roster_excluded` not true.
49+
- `GET /api/congress/members/:pol` — Single politician document ([`getPol`](../controller/congress/pol.js)); requires authentication.
50+
- `GET /api/congress/election-dates` — Election dates (snapshot / fallbacks).
5051

5152
### **[`Payments`](../routes/api/payments.js)** [(Stripe)](https://stripe.com)
5253

5354
- [`sendPayment`](../controller/payments/createPayment.js) sends payment to [`Stripe`](https://stripe.com/docs/payments),
5455
- [`setupIntent`](../controller/payments/setupIntent.js) creates a "payment intent" object
5556
- [`setPaymentMethod`](../controller/payments/setPaymentMethod.js) creates a "payment method" i.e. user's credit card
57+
- **Roster exclusion**: `POST /api/payments/celebrations/:customer_id` rejects excluded `pol_id` with HTTP `400` and `code: POL_ROSTER_EXCLUDED` **before** Stripe payment intent creation (see [`polRosterEligibility`](../services/congress/polRosterEligibility.js)).
5658

5759
> **📖 For comprehensive payment processing documentation, see [`docs/payment-processing.md`](./payment-processing.md)**
5860
> **📖 For webhook processing details, see [`docs/webhooks.md`](./webhooks.md)**
@@ -85,4 +87,5 @@ Each category is a link to its [`Routes`](../routes/) or relevant folder.
8587
- [Webhook System](./webhooks.md) - Real-time event processing
8688
- [Background Jobs](./background-jobs.md) - Automated monitoring and updates
8789
- [Email System](./email-system.md) - Email notifications
88-
- [Bitcoin Donations](./bitcoin-donations.md) - Cryptocurrency support
90+
- [Bitcoin Donations](./bitcoin-donations.md) - Cryptocurrency support
91+
- [Pol roster exclusion](../specs/pol-roster-exclusion.md) - Policy exclusions for selectable roster and new Celebrations

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ This directory contains comprehensive documentation for the POWERBACK.us platfor
3131
- **[FEC Compliance Guide](./fec-compliance-guide.md)** - Comprehensive FEC compliance requirements
3232
- **[Donation Limits](./donation-limits.md)** - Annual and election cycle resets
3333
- **[Donor Validation](./donor-validation-comprehensive.md)** - FEC "best efforts" validation system
34+
- **[Pol roster exclusion](../specs/pol-roster-exclusion.md)** - Policy exclusions from selectable roster and new Celebrations (vs `has_stakes` watchers)
3435

3536
### Celebration Lifecycle
3637

docs/background-jobs.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ All background jobs are orchestrated through the `runWatchers` system, which:
6666
- Tracks challenger status changes (appeared, disappeared, reappeared)
6767
- Tracks incumbent dropouts
6868
- Updates `has_stakes` flags based on competitive race status
69+
- **Policy note**: POWERBACK exclusions from the selectable roster use `Pol.roster_excluded`, not watcher toggles on `has_stakes` alone. See [`specs/pol-roster-exclusion.md`](../specs/pol-roster-exclusion.md).
6970
- Sends email alerts to users in affected districts
7071

7172
**Email Alerts**:
@@ -295,3 +296,4 @@ await watcher.forceWarningEmails();
295296
- [Election Date Notifications](./election-dates.md) - Election date change notifications
296297
- [Email System](./email-system.md) - Email notifications sent by watchers
297298
- [Status Ledger System](./status-ledger-system.md) - Celebration status tracking
299+
- [Pol roster exclusion](../specs/pol-roster-exclusion.md) - Roster exclusion policy vs watcher `has_stakes`

docs/docking-pols-runbook.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,4 @@ If Senators appear in the docking collection, the Congress.gov API's `currentMem
9090
- `scripts/add-members-to-docking.js` -- Staging script for specific members
9191
- `services/utils/dockingManager.js` -- DockingManager class and CLI
9292
- `jobs/houseWatcher.js` -- Background job that auto-stages new members
93-
- `models/Pol.js` -- Pol schema definition
93+
- `models/Pol.js` -- Pol schema definition (`has_stakes`, policy `roster_excluded`; see [`specs/pol-roster-exclusion.md`](../specs/pol-roster-exclusion.md))

docs/election-dates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ The system includes robust error handling:
196196

197197
- `User` collection with `email`, `firstName`, `compliance`, `ocd_id` fields
198198
- `Celebration` collection with `donatedBy`, `pol_id`, `resolved`, `defunct`, `paused` fields
199-
- `Pol` collection with `id`, `roles.state`, `has_stakes` fields
199+
- `Pol` collection with `id`, `roles.state`, `has_stakes`, and optional policy fields `roster_excluded`, `roster_exclusion_*` ([`specs/pol-roster-exclusion.md`](../specs/pol-roster-exclusion.md))
200200

201201
## Testing
202202

0 commit comments

Comments
 (0)