Freeze voting capacity alongside user-owned ballots
Outcome: retain user-owned ballots and one ordinary ballot per electorate user, while freezing each participant's voting capacity when a run starts. Store role/voting-right identity, party/independent status and replacement-seat context in the Rust relational electorate; render voting role/party attribution from that immutable capacity rather than mutable Rails voting-right records. Constraints: preserve authentication, manual actor attribution, casting votes, anonymous-result suppression and existing shared UI. No Rails ballot/snapshot persistence, dual writes or changes to voting semantics. Existing runs without recorded capacity must not be presented as having historically verified capacity; use an explicit unavailable state rather than inventing historical context. Extend existing contracts, projections and tests, not parallel implementations. Acceptance: new runs require complete validated capacity for every electorate user; persistence and repeated-start compatibility retain immutable capacity; later party/role/right changes or removed source records do not change or drop recorded vote attribution; replacement/casting/manual/anonymous behavior remains intact; focused Rust/PostgreSQL and Rails regressions, real HTTP round trip, format/check and changed-Ruby RuboCop pass. Implementation decisions: a new normalized voting_capacities table references the per-run electorate; it freezes voting-right/role/list identity, voter name, party ID/name/abbreviation and independence, represented seat role and replacement chain. New starts require complete capacity and reject changed capacity on repeated starts. Pre-capacity runs retain an explicit empty map; partial capacity is invalid and no mutable-record backfill is attempted. Existing shared views use a lightweight frozen role projection, including an explicit unavailable projection for old capacity. Verified outcome: all 21 Rust unit/authentication/logging/shared-contract and dedicated PostgreSQL tests pass. Broader Rails validation ran 204 examples with zero failures and four expected HTTP skips; all four HTTP examples then passed alongside all nine system examples through the real isolated Rust service (13 examples, zero failures/pending), for 213 distinct successful Rails/system examples. Coverage includes actual party swaps followed by deleted source roles, deputy-seat capture, old unavailable capacity, casting/manual behavior and anonymous/public/bigscreen presentation. Cargo format/check, Clippy, rustdoc, all 23 changed Ruby files under RuboCop, six-locale key validation, Zeitwerk and diff whitespace checks pass. Static feature review found no actionable findings. Architecture source and service README are updated; Typst 0.15.1 compiled the architecture PDF and all ten rendered pages were visually reviewed. Operational boundary: no commits or deployment performed. Apply the new additive Rust migration and release Rails/Rust together because the development version-1 contract now requires voting_capacities. Existing pre-capacity runs are intentionally not backfilled. Validation emitted existing PostgreSQL ICU collation-version and tooling-deprecation warnings; no environment/configuration changes were made.
Checklist
3 of 3 complete- Verify immutable start/retry, replacement/casting/manual/anonymous regressions and real Rails–Rust persistence round trip; pass relevant lint/build checks and update architecture documentation.
- Use frozen capacity throughout shared Rails voter/party projections and verify attribution survives source role/right changes or deletion.
- Capture and validate immutable per-user voting capacity in the Rust contract, domain and relational electorate, including migration behavior for pre-capacity runs.
- Area
- Kaukus
- Kind
- Feature
- Task ID
tsk_c9umywwyvp- Updated