02 โ Persist transactional per-effect outbox delivery state
## Objective Make PostgreSQL authoritative for partial completion and recovery while preserving accepted-command atomicity and receipt identity. ## Code locations Existing: app/models/voting_engine/outbox_event.rb; app/services/voting_engine/{command_processor,destroy_meeting_state}.rb; db/migrate/20260828090000_create_voting_engine_core.rb (reference only); db/schema.rb. Proposed: new additive migration; app/models/voting_engine/outbox_delivery.rb; effect-plan persistence service agreed in task 01. ## Changes - Add event delivery version/context metadata without changing existing semantic result keys. Preserve ve_one_command_event uniqueness and immutable receipt/event identity. - Proposed voting_engine_outbox_deliveries: event FK, stable effect_key, handler version, ordering lane, minimal immutable arguments; states pending/processing/failed/blocked/delivered/skipped; attempts, next_attempt_at, last_attempt_at, claim token/lease expiry, completion timestamp, sanitized error class/code and skip/block reason, timestamps. - Unique (outbox_event_id, effect_key), FK, valid-state/nonnegative-attempt checks; indexed due-work/expired-lease and pending-event queries. - Persist accepted receipt, event and initial effect plan within the same CommandProcessor savepoint. Any plan persistence failure rolls back domain changes. Replaying a command returns existing state, never resets deliveries. - Aggregate delivered_at is written only after every required effect succeeds or is explicitly not applicable. Failed/blocked work prevents aggregate completion; distinguish skipped from actually sent in diagnostics. - Allow delivery bookkeeping changes only, not semantic payload mutation. Foreign-key deletion/DestroyMeetingState must remove event-owned rows safely. Update separate-connection test teardown that currently deletes events directly. - Leave legacy unplanned rows distinguishable and unresolved for the dedicated reconciliation task; do not bulk mark historical work delivered. ## Acceptance and tests Proposed spec/models/voting_engine/outbox_delivery_spec.rb: DB uniqueness and constraints, partial completion, aggregate state, immutability and cleanup. Extend spec/services/voting_engine/{engine,poll_execution,destroy_meeting_state}_spec.rb: outbox/plan failure rollback, rejected commands create no work, duplicate receipt replay creates no deliveries. Run RAILS_ENV=test bundle exec rails db:migrate; RAILS_ENV=test bundle exec rails zeitwerk:check; bundle exec rspec spec/models/voting_engine/outbox_delivery_spec.rb spec/services/voting_engine/engine_spec.rb spec/services/voting_engine/poll_execution_spec.rb spec/services/voting_engine/destroy_meeting_state_spec.rb. RuboCop all changed Ruby; regenerate schema through Rails. ## Depends on https://task.matda.me/tasks/tsk_2qravp6n9b โ effect contracts and context versioning. ## Questions None currently blocking. Legacy ambiguity and secret/provider-specific storage are handled in their own tasks; do not invent a retention purge policy or treat skipped effects as successful sends.
Checklist
0 of 0 completeNo checklist items.
- Area
- Kaukus
- Kind
- Feature
- Priority
- Medium
- Task ID
tsk_5t6us3yxjk- Updated