08 — Make Teams lifecycle delivery durable and resolve ambiguous starts
## Objective An accepted engine-backed meeting start/end persists recoverable Teams intent, without duplicate bots when requests succeed remotely but acknowledgements are lost. ## Existing code and limitation app/controllers/meetings_controller.rb#start_with_meeting_room_selected; app/services/kaukus_teams/{lifecycle,client,meeting_url}.rb; app/models/kaukus_teams_session.rb; app/jobs/kaukus_teams/{poll_session_job,reconcile_sessions_job}.rb; docs/kaukus_teams_operations.md. Teams URL is currently request-only. Existing generation-bound state, start_uncertain, stop_pending and poll sweep are valuable. Documentation explicitly says POST start has no idempotency key or lookup/list endpoint, so fully automatic duplicate-free recovery is impossible today. ## Changes - Coordinate paired service support for a stable event/effect idempotency key and lookup/replay of the original session. Record exact remote contract/version and service-side work/evidence here; do not assume an arbitrary header is supported. - Persist validated initial meeting URL in protected worker-readable storage before accepted operation returns, atomically associated with event/intent. Keep secrets out of regular event JSON, receipt input, logs, metrics and debug pages. Preserve municipality/room reservation transaction and original start context. - Move initial Teams lifecycle effects from controller continuation to consumer. Preserve explicit user rejoin/retry semantics; do not accidentally reinterpret those as replay of the original start. - Carry one delivery identity through request, acknowledgement and remote lookup. Inspect actual lifecycle state instead of treating a rescued exception/session return as success. - Reuse session generation fencing, start uncertainty, durable stop intent and reconciliation. Ending while start is in flight must retain stop intent and eventually clean up. A blocked start must not indefinitely prevent teardown. - Until the upstream contract is available, retain safe blocked/manual reconciliation for ambiguous starts and explicitly report that strict source acceptance is unmet. Never blind POST retries, duplicate bots, or fake successful delivery. - A durable handoff to session polling can complete a delivery only when its persisted state guarantees recovery independently of enqueue success. ## Acceptance and tests Extend spec/services/kaukus_teams/lifecycle_spec.rb, spec/jobs/kaukus_teams/poll_session_job_spec.rb and meetings controller specs; proposed outbox Teams consumer spec. Cover crash before enqueue, before POST, remote success before local ack, timeout/unknown remote ID, duplicate jobs, concurrent start/stop, generation changes, new meeting generation, secret redaction and sweep recovery. Contract-test paired service idempotency: repeated request produces one bot/session and recovers original ID. Run bundle exec rspec spec/services/kaukus_teams spec/jobs/kaukus_teams spec/controllers/meetings_controller_spec.rb; RuboCop changed Ruby. ## Depends on https://task.matda.me/tasks/tsk_2qravp6n9b — guarantee/context contract. https://task.matda.me/tasks/tsk_beu5nmfqfj — worker/ledger. Coordinate removal of direct calls and callback ownership with tasks 04/06. ## Questions - Is coordinated modification/release of the Teams service authorized to add start idempotency and lookup? Which paired repository/release owns that contract? - If unavailable, is an explicit exception allowing blocked operator reconciliation approved? This weakens automatic eventual-delivery acceptance and must not be assumed. - Confirm the approved protected storage/key-management approach for persisting Teams meeting URLs for background recovery; do not put them in plaintext outbox metadata.
Checklist
0 of 0 completeNo checklist items.
- Area
- Kaukus
- Kind
- Feature
- Priority
- Medium
- Task ID
tsk_z2yguhkg9b- Updated