Appearance
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
| Requirement | Evidence | Result |
|---|---|---|
| Attempt route proof | TestStartAttemptHTTP proves trusted start, snapshot copy, answer save, event append, submit/grading, result read, and answer-key redaction through HTTP handlers. | Passed |
| Idempotency proof | TestStartAttemptHTTPReplaysIdempotencyKeyAfterSubmit and TestAttemptServiceStartReplaysIdempotencyKeyAfterSubmit return the original graded attempt with created=false; open-attempt reuse and durable key guards are also covered. | Passed |
| Timeout/retake/result visibility | TestAttemptServiceTimeoutRetakeResultVisibilityWorkflow and TestAttemptServiceStudentResultVisibilityModes cover timeout submission, submitted-attempt retake limits, manual release, after-close visibility, immediate visibility, and score redaction. | Passed |
| Non-default Gateway workflow | Disposable 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 replay | Reusing svc010-gateway-idempotency-key after grading returns the original attempt with created=false, GRADED, and the exam_start_adapter headers. | Passed |
| Browser read smoke | Chromium 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 rollback | Gateway 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-serviceowns attempts, copied exam/question snapshots, answers, grading, attempt events, idempotency, timeout, retake limits, and result visibility from copied policy fields.exam-serviceremains the owner of published exam snapshots and runtime access decisions; attempt-service accepts only trusted adapter headers for public-compatible starts.question-bank-serviceremains 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.shThe 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 indeploy/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 rerunningmake test-attempt-routesand the relevant smoke checks.
Remaining Work
- Exercise a real seeded
exam-servicepublished-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.