- max_attempts enforcement in start_attempt: raises AttemptLimitError when
quota of finalized attempts is exhausted (in_progress not counted)
- Timer enforcement: start_attempt sets expired_at = now + time_limit_seconds;
check_attempt_not_expired auto-finalizes and raises AttemptExpiredError on deadline breach
- finalize_timed_out_attempt: grades partial responses, sets TIMED_OUT status
- expire_timed_out_attempts Celery task: idempotent sweep of overdue in-progress attempts
- Views: 409 on limit breach (start), 410 on expired response submit; timed_out result on submit
- 14 unit + integration tests covering policy boundary conditions and API responses
Co-Authored-By: Paperclip <noreply@paperclip.ing>