05 — Deliver poll and vote presentation through replay-safe consumers
## Objective Committed poll starts and votes refresh voters, secretaries and public clients through recoverable consumers, without duplicate DOM results or reliance on a continuous polling chain. ## Code locations app/controllers/secretary/polls_controller.rb (engine lifecycle/casting actions); app/controllers/{politician,secretary}/votes_controller.rb; app/models/poll.rb broadcasting methods; app/jobs/broadcast_vote_job.rb; app/views/secretary/polls/{create,end}.turbo_stream.haml and existing poll partials; app/views/public_info/polls/_polls.html.haml; app/views/politician/cases/_polls_stream.html.haml. Proposed shared poll presentation service and outbox consumer under app/services/voting_engine/. ## Changes - Route start/end/cancel/reset/unanimous and all five vote commands to narrow current-state consumers. Include casting-weight and manual-vote metadata changes, including ended-poll results. - Reload current committed state and respect execution/run generation. Delayed start/end/reset work must not reopen a closed poll, clear a newer active poll, or render stale receipt state. - Replace append-oriented lifecycle effects with stable container/row replacements. Preserve secretary poll controls/proposal info/poll cards, public current/result views, politician voting frame/current/ended/tally targets and participant updates for bias effects (shared with task 06). - Reuse/refactor existing broadcasters and partials. Consolidate duplicated create/end stream rendering rather than copying markup; use a shared ViewComponent for reused logic. Search components/helpers and read styleguides first. - Remove controller/job-owned publications for accepted engine commands. Keep direct responses, current-user modal closure, validation/failure messages and exact-resource authorization; do not broadcast flash/modal actions to everyone. - Remove the BroadcastVoteJob restart-loop dependency. Extract one-shot tally rendering; events trigger delivery. Existing job writes caches before sending, retries false and fingerprints omit double_vote/manual metadata: caches must not gate correctness. Keep old queued jobs compatible as one-shot refreshes without restarting. - Use municipality locale and existing confidential per-session refetch pattern (Poll#broadcast_politician_refetch!). Workers must not decrypt or push session-specific private content. ## Acceptance and tests Proposed spec/services/voting_engine/outbox/poll_presentation_spec.rb. Extend spec/jobs/broadcast_vote_job_spec.rb and secretary poll/vote/politician vote controller specs. Verify no direct publication, eventual consumer publication, all expected audiences, partial-send retry, idempotent repeated DOM replacement, one result/card per ID, all vote changes, start-cancel-restart/reset races, missing resources and confidential refetch. Add focused proposed live outbox system coverage for voter controls. Run bundle exec rspec spec/jobs/broadcast_vote_job_spec.rb spec/controllers/secretary/polls_controller_spec.rb spec/controllers/secretary/votes_controller_spec.rb spec/controllers/politician/votes_controller_spec.rb spec/services/voting_engine/outbox/poll_presentation_spec.rb; RuboCop changed Ruby. ## Depends on https://task.matda.me/tasks/tsk_2qravp6n9b — routes. https://task.matda.me/tasks/tsk_beu5nmfqfj — workers (includes ledger). https://task.matda.me/tasks/tsk_5fdrm4pptn — callback ownership. Coordinate shared presentation ownership with task 06; deploy together, not partial production activation. ## Questions None blocking. Reliable Action Cable publication and replay-safe state are required; it is not an acknowledgement protocol for every disconnected browser. Chair-selected outcome command migration is owned by tsk_zxbg4uyua2, not this task.
Checklist
0 of 0 completeNo checklist items.
- Area
- Kaukus
- Kind
- Feature
- Priority
- Medium
- Task ID
tsk_g53qefv87e- Updated