- getQuizQuestions: only return answer/explanation after user has submitted
- findOne: remove questions include (use getQuizQuestions instead)
- State mapping: lifecycleStatus compatible with legacy status field
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace hardcoded 'https://api.deepseek.com/v1/models' with config lookup
- Single source of truth via ai.deepseek.baseUrl (DEEPSEEK_BASE_URL env)
- Add ConfigService dep to UserAiService constructor + test mock
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Rename checkAndReserve→checkQuota (method only reads, does not reserve)
- Add doc comment: incrementJobCount is the actual quota reservation
- Update user-ai.service.spec.ts references
- Add comments for apiKeyMode/credentialId reassignment in budget fallback
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1. heartbeat: locked→running transition on first heartbeat
2. submitResult: validate job is locked/running before accepting
3. submitFailure: validate job is locked/running before accepting
4. resolveCredentialForJob: update lastUsedAt on credential
5. pollJobs: filter by capabilities (snapshotVersion + outputSchemaVersion)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>