Skip to content

fix(backend): bucket Minnesota/Alaska outside-group breakdown caps per (committee, direction) - #549

Merged
shu1513 merged 1 commit into
mainfrom
claude/peaceful-mendeleev-850c16
Aug 6, 2026
Merged

shu1513 merged 1 commit into
mainfrom
claude/peaceful-mendeleev-850c16

Conversation

@shu1513

@shu1513 shu1513 commented Aug 6, 2026 •

Copy link
Copy Markdown
Owner

Problem

The Minnesota and Alaska outside-group contribution aggregators applied maxBreakdownsPerCategory globally — sorting and slicing across all donors and industries from every outside group at once — instead of per (committee, direction) bucket like Kentucky and Vermont do.

With up to 20 groups × 2 directions, smaller groups could end up with zero donor rows, and industry rows were genuinely dropped. Alaska was worst: its sync passes a cap of 20 (alaskaCandidateFinanceSync.ts:92), spread across up to 40 buckets.

Separately, Vermont's collectOutsideClassifications read post-cap donor rows, so any donor past the cap never reached the manual label queue.

Changes

Minnesota (minnesotaOutsideGroupContributionAggregator.ts) and Alaska (alaskaOutsideGroupContributionAggregator.ts):

  • toBreakdowns now buckets donors by (committee, direction) and applies the cap inside each bucket, matching the Kentucky/Vermont shape. Buckets are emitted in a stable key order.
  • Industry rows are no longer capped at all. They are naturally bounded per bucket by FINANCE_INDUSTRY_SLUGS (21 entries), following the Ohio/Maryland pattern documented on capDonorBreakdowns in ohioCandidateFinanceSync.ts (commit 893c950).

Vermont (vermontOutsideGroupContributionAggregator.ts, vermontCandidateFinanceSync.ts):

  • The aggregator now returns outsideDonorClassifications, collected from every donor at or above minIndustryAmount before the display cap.
  • The sync consumes that instead of its own collectOutsideClassifications helper (deleted), which re-classified only surviving post-cap rows. Same amount threshold as before — the only change is coverage.

No cap defaults or wiring changed; this is purely where the cap is applied.

Tests

  • New per-bucket cap test for Minnesota and for Alaska: cap of 1 with two groups, asserting each group keeps its own top donor and that all four industry rows survive uncapped.
  • New Vermont test proving a donor cut by the display cap still appears in outsideDonorClassifications.
  • Updated the Vermont sync test fake for the new field, plus an assertion that classifications reach the finance_label_classifications upsert.
npx vitest run src/pipeline/minnesotaFinance src/pipeline/alaskaFinance src/pipeline/vermontFinance
  → 30 files, 216 tests passed
npx vitest run tests/scheduler/vermontCandidateFinanceSyncScheduler.test.ts
  → 9 tests passed
npm run typecheck
  → clean

Follow-up (not in this PR)

Kentucky and Vermont still cap industry rows per bucket. Harmless at their default cap of 50, but a two-line change each if you want strict Ohio parity.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Outside-group contribution breakdowns are now organized separately by committee and support/oppose direction.
    • Donor display limits apply independently to each group, while industry breakdowns include all qualifying industries.
    • Vermont finance reporting now preserves donor classifications even when donors fall outside display limits.
  • Bug Fixes

    • Improved consistency and completeness of donor classification data in Vermont finance reporting.
  • Tests

    • Added coverage for grouped breakdown limits, uncapped industry results, and complete donor classifications.

Minnesota and Alaska applied maxBreakdownsPerCategory across all donors
and industries from every outside group at once, so smaller groups could
end up with zero donor rows and industry rows were genuinely dropped
(Alaska: cap 20 spread over up to 40 buckets). Bucket by (committee,
direction) before capping, matching Kentucky/Vermont, and stop capping
industry rows entirely -- they are bounded per bucket by the 21-entry
industry slug set, per the Ohio/Maryland pattern.

Vermont collected its manual-label classifications from post-cap donor
rows, so donors past the cap never reached the label queue. Move
collection into the aggregator, before the cap, and return it as
outsideDonorClassifications.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2dec9180-e6ce-4a07-bea1-5361387fddd0

📥 Commits

Reviewing files that changed from the base of the PR and between ebf1a19 and 43922be.

📒 Files selected for processing (8)
  • backend/src/pipeline/alaskaFinance/alaskaOutsideGroupContributionAggregator.ts
  • backend/src/pipeline/minnesotaFinance/minnesotaOutsideGroupContributionAggregator.ts
  • backend/src/pipeline/vermontFinance/vermontCandidateFinanceSync.ts
  • backend/src/pipeline/vermontFinance/vermontOutsideGroupContributionAggregator.ts
  • backend/tests/pipeline/alaskaFinance/alaskaOutsideGroupContributionAggregator.test.ts
  • backend/tests/pipeline/minnesotaFinance/minnesotaOutsideGroupContributionAggregator.test.ts
  • backend/tests/pipeline/vermontFinance/vermontCandidateFinanceSync.test.ts
  • backend/tests/pipeline/vermontFinance/vermontOutsideGroupContributionAggregator.test.ts

📝 Walkthrough

Walkthrough

The PR groups Alaska and Minnesota breakdowns by committee and support direction. Donor caps apply per group, while industry rows remain uncapped. Vermont aggregation now returns donor classifications, and synchronization consumes them.

Changes

Outside-group finance processing

Layer / File(s) Summary
Grouped donor and industry breakdowns
backend/src/pipeline/alaskaFinance/alaskaOutsideGroupContributionAggregator.ts, backend/src/pipeline/minnesotaFinance/minnesotaOutsideGroupContributionAggregator.ts, backend/tests/pipeline/alaskaFinance/alaskaOutsideGroupContributionAggregator.test.ts, backend/tests/pipeline/minnesotaFinance/minnesotaOutsideGroupContributionAggregator.test.ts
Alaska and Minnesota bucket breakdowns by committee and support direction. Donor limits apply per bucket. Industry breakdowns are uncapped and tested across all buckets.
Vermont aggregation classification contract
backend/src/pipeline/vermontFinance/vermontOutsideGroupContributionAggregator.ts, backend/tests/pipeline/vermontFinance/vermontOutsideGroupContributionAggregator.test.ts
Vermont aggregation returns merged classifications for qualifying donors, including donors excluded from capped display breakdowns. Empty results return an empty classification array.
Vermont sync classification integration
backend/src/pipeline/vermontFinance/vermontCandidateFinanceSync.ts, backend/tests/pipeline/vermontFinance/vermontCandidateFinanceSync.test.ts
Vermont synchronization uses outsideDonorClassifications instead of classifying capped breakdowns locally. Tests verify the Sierra Club classification insert.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VermontOutsideGroupContributionAggregator
  participant ClassificationMergeService
  participant VermontCandidateFinanceSync
  participant FinanceLabelClassificationInsert
  VermontOutsideGroupContributionAggregator->>ClassificationMergeService: merge qualifying donor classifications
  ClassificationMergeService-->>VermontOutsideGroupContributionAggregator: merged classifications
  VermontOutsideGroupContributionAggregator-->>VermontCandidateFinanceSync: outsideDonorClassifications
  VermontCandidateFinanceSync->>FinanceLabelClassificationInsert: insert donor classifications
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: per-committee and direction bucketing for Minnesota and Alaska outside-group breakdown caps.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/peaceful-mendeleev-850c16

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@shu1513
shu1513 merged commit 3e54f5a into main Aug 6, 2026
6 checks passed
@shu1513
shu1513 deleted the claude/peaceful-mendeleev-850c16 branch August 6, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant