Skip to content

Attempt Public Idempotency Proof

Status: accepted-bounded-next-slice-not-complete.

This proof closes the SVC-010-attempt-public-idempotency-proof contract slice while keeping public attempt route promotion gated.

Evidence

RequirementEvidenceResult
Attempt route proofTestStartAttemptHTTP proves trusted start, snapshot copy, answer save, event append, submit/grading, result read, and answer-key redaction through HTTP handlers.Passed
Idempotency proofTestStartAttemptHTTPReplaysIdempotencyKeyAfterSubmit and TestAttemptServiceStartReplaysIdempotencyKeyAfterSubmit return the original graded attempt with created=false; open-attempt reuse and durable key guards are also covered.Passed
Timeout/retake/result visibilityTestAttemptServiceTimeoutRetakeResultVisibilityWorkflow and TestAttemptServiceStudentResultVisibilityModes cover timeout submission, submitted-attempt retake limits, manual release, after-close visibility, immediate visibility, and score redaction.Passed
Non-default Gateway workflowDisposable runtime-access fixture -> in-memory attempt-service -> Gateway passes list, trusted start, answer save, event, submit, and post-submit result. final-live/ records the Gateway headers and JSON bodies.Passed, local fixture only
Idempotency replayReusing svc010-gateway-idempotency-key after grading returns the original attempt with created=false, GRADED, and the exam_start_adapter headers.Passed
Browser read smokeChromium fetches student list, detail, events, and result through the non-default Gateway table. summary.json records native_read headers and page.png records the same-origin shell.Passed, not exam-room UI parity
Default rollbackGateway restarted with deploy/gateway/routes.json and an unreachable legacy base returns 502 through exams/legacy_proxy and attempts/legacy_proxy.Passed

Ownership Boundary

  • attempt-service owns attempts, copied exam/question snapshots, answers, grading, attempt events, idempotency, timeout, retake limits, and result visibility from copied policy fields.
  • exam-service remains the owner of published exam snapshots and runtime access decisions; attempt-service accepts only trusted adapter headers for public-compatible starts.
  • question-bank-service remains the source of question authoring data; attempt-service never reads current question rows.
  • Profile, IAM, classroom, analytics, notification, and gateway data remain outside attempt-service storage and are accessed only through contracts.

Verification

bash
GOTOOLCHAIN=go1.25.11 go test ./services/attempt-service/... -count=1
make test-attempt-routes
ATTEMPT_LIVE_SELF_TEST=1 bash scripts/test/attempt-live-smoke.sh
ATTEMPT_BROWSER_SELF_TEST=1 bash scripts/test/attempt-browser-smoke.sh

The focused tests, route guards, self-tests, non-default local workflow, browser read smoke, replay assertion, and default rollback assertion passed.

Artifacts:

  • output/qa/attempt-public-idempotency-proof-20260713/final-live/
  • output/playwright/attempt-public-idempotency-proof-20260713/
  • output/qa/attempt-public-idempotency-proof-20260713/rollback-default/

Route State And Rollback

  • Default /api/exams/*/start, /api/student/attempts, and /api/attempts* remain legacy-proxied in deploy/gateway/routes.json.
  • Native attempt routes remain in non-default rehearsal tables only.
  • This is a non-default, fixture-backed proof only. The default table remains legacy-proxied and no production or default-route promotion follows from it.
  • Rollback is switching back to deploy/gateway/routes.json, then rerunning make test-attempt-routes and the relevant smoke checks.

Remaining Work

  • Exercise a real seeded exam-service published-exam decision rather than the disposable runtime-access fixture.
  • Exercise the actual student exam-room UI rather than the Gateway health-page same-origin shell.
  • Preserve the exam-service runtime decision marker and idempotency context at every adapter boundary.

Go-platform documentation is generated from repository Markdown.