Skip to content

Exam Runtime Access Task

Agent workflow: follow README.md for Audit -> Investigate -> Code -> Test -> Fix; keep this pack's writable scope and verification commands authoritative.

Dispatch type: orchestrator

Owner: exam-service.

Goal:

Expose a native exam runtime snapshot/access-decision contract that attempt-service can consume before any public attempt-start cutover.

Legacy source evidence:

  • /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exams/**
  • /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma
  • student exam-start callers under /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/web.

Writable files:

  • services/exam-service/**
  • contracts/openapi/services/exam-service.yaml
  • docs/api/exam-service.md
  • docs/agents/service-tasks/exam-runtime-access.md

Orchestrator-owned files:

  • Attempt-service consumer integration, gateway route tables, route promotion, and frontend/browser smoke artifacts.

Tasks:

  • Done locally: add POST /v1/exams/{id}/runtime-access for published exams with question snapshots by value and a startInput payload compatible with attempt-service.
  • Done locally: add access-decision fields for assignment, link/password, open/closed window, login/guest policy, attempt limit, and organization scope.
  • Done locally: add exam_assignments read-model migration plus a verifier interface for classroom/org membership checks without cross-DB reads.
  • Done locally: add tests covering assigned student via verifier, link access, password failure, closed exam, body studentId rejection, and missing snapshots.
  • Done locally: block assigned-exam starts when the classroom/IAM verifier is missing, returning ASSIGNMENT_DECISION_UNAVAILABLE with classroomMembership in pendingPolicyChecks.
  • Done locally: add runtime-access tests for open/close window boundaries, missing access links, unassigned exams, and overdue assignments.
  • Done locally: runtime-access now treats an access link with expiresAt == evaluatedAt as inactive, matching legacy expiresAt <= now behavior before any attempt-service call.
  • Done locally: wire production verifier clients behind IAM_SERVICE_URL / IAM_SERVICE_BASE_URL and CLASSROOM_SERVICE_URL / CLASSROOM_SERVICE_BASE_URL. The verifier checks IAM active organization membership through the internal single-account check endpoint and checks assigned classroom membership with a student-scoped classroom-service read, without cross-service DB joins.
  • Done locally: attempt-service consumer hardening now enforces retake/open-attempt/access-link counts after the trusted exam-service decision, requires the trusted decision marker plus idempotency/correlation context, persists start replay keys per exam/student, and guards concurrent duplicate IN_PROGRESS rows.
  • Keep public /api/exams/:id/start legacy until attempt-service and gateway integration tests prove the full flow.

Acceptance:

  • Attempt-service receives all access policy inputs without reading exam DB.
  • Question payloads are snapshots, not live question-service rows.
  • Gateway route rehearsal does not bypass exam-service access decisions.
  • Runtime resolver returns data.startInput; gateway/BFF must pass only that payload to attempt-service.
  • Public start promotion also requires exam-start-adapter.md route proof, browser/runtime smoke, and rollback evidence; service tests alone are not enough.

Verification:

  • GOTOOLCHAIN=go1.25.11 go test ./services/exam-service/... ./services/attempt-service/...
  • GOTOOLCHAIN=go1.25.11 go test ./services/iam-service/... ./services/exam-service/... -count=1
  • make test-attempt-routes
  • Use docs/agents/service-tasks/exam-start-adapter.md to add/run the adapter-specific route test before /api/exams/:examId/start promotion.

Go-platform documentation is generated from repository Markdown.