UX-RUNTIME-CANDIDATE-DELIVERY.md
This document defines the UX contract for candidate/runtime delivery.
Goal:
- keep delivery calm and reliable under normal connectivity
- keep high-stakes enforcement explicit (lockdown, verification rigor)
- design now so offline delivery can be added later without rewriting the runtime UX model
Capability Baseline (Validated 2026-02-25)​
Backend-live now:
- Launch/redeem and session lifecycle:
POST /api/v1/assignments/redeemPOST /api/v1/evaluations/\{id\}/sessionsPOST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/answersPOST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/submitPOST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/eventsGET /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/lockdown/probe
- Post-attempt forensic views:
GET /api/v1/submissions/\{id\}(supportsview=candidate,lang)GET /api/v1/session-attempts/{submissionId}(+ export)
- Assignment monitor handoff for operations:
GET /api/v1/assignments/\{id\}/monitor
Foundational runtime behavior already enforced in backend:
- idempotent create/answer/submit writes
- assignment-linked overrides (attempt/time/feedback) frozen into session
- expiry/reaper and runtime telemetry capture
- lockdown enforcement when security config requires it
Target-state (not yet first-class):
- offline capture and sync APIs
- explicit proctor command endpoints (pause/resume/terminate)
- dedicated candidate read model endpoint for runtime hydration/resume state
0. Runtime Doctrine (MUST)​
- Candidate confidence first: always show "what state am I in" (ready, in progress, syncing, submitted, closed).
- Server is authoritative for timing and final status.
- Local saves are useful, but not equivalent to durable submit.
- High-stakes checks must be visible in plain language, not hidden in technical errors.
- Runtime and forensic history are separate surfaces: execution now, explanation later.
0.1 Field Device Contract (MUST)​
Delivery is mobile-first and field-first:
- all critical runtime actions must be reachable on phone and iPad
- touch targets and spacing must support gloved/noisy environments
- no hover-only affordances for critical runtime actions
- keyboard and screen-reader paths must remain valid on iPad and desktop
reference:
/docs/ux/UX-MOBILE-AND-TASK-TIER-POLICY.md/docs/ux/UX-ACCESSIBILITY-STANDARDS-AND-GUARDRAILS.md
0.2 Runtime Interface Guardrails (MUST)​
- Candidate state must always be explicit (
launch_ready,in_progress,syncing,submitted,closed_*). - Sync status must be visible and actionable (
pending,retrying,failed) without losing local answers. - Submit UX must be retry-safe and idempotency-aware; duplicate taps cannot create duplicate submits.
- Critical actions must remain reachable via keyboard and touch.
- Error language must be plain and specific (for example lockdown failure vs generic submit failure).
- Offline/poor-connectivity messaging must prioritize recovery next step over technical diagnostics.
0.3 Candidate-Facing Criticality (MUST)​
Delivery is a primary end-user surface and carries stricter UX burden than admin-only screens.
Hard rules:
- No reduced completion path on mobile: start, answer, review, and submit must be fully available on phone/iPad.
- Time, expiry, and submit-risk signals must be explicit in text and announced accessibly (not color-only).
- Interruption resilience is mandatory: background/app switch/network loss must not silently discard in-progress work.
- Navigation and response controls must remain operable by touch, keyboard, and screen reader.
- High-stakes verification/lockdown failures must provide clear recovery actions, not generic technical copy.
1. Page Model​
Primary runtime screens:
Launch(redeem + preflight)In Session(answering + navigation + sync state)Submit Review(final check + submit action)Completion(submitted/closed outcome)Attempt Review(post-submission candidate-safe results)
Supporting overlays:
- reconnect banner
- expiry warning
- lockdown requirement modal
- sync backlog panel (target-state for offline mode)
2. Launch and Preflight​
2.1 Entry Modes​
Mode A: assignment link flow
- call
POST /assignments/redeemwith token - response provides:
session- session bearer
token securityConfig
Mode B: internal/managed launch
- call
POST /evaluations/\{id\}/sessions - include
userId(currently required by backend), optionalversionId,assignmentId,seed,questionOrder
2.2 Launch UX Rules​
- show a deterministic preflight card:
- attempt limits
- time limit (if present)
- run label (if present)
- security level (derived from
securityConfig)
- primary CTA:
Start assessment
- secondary CTA:
Cancel
2.3 Lockdown Probe​
When security config indicates lockdown requirements:
- run
GET /evaluations/\{id\}/sessions/\{sessionId\}/lockdown/probebefore entering question canvas - if probe fails (
400):- block entry
- show plain-language remediation ("required secure browser check failed")
- provide
Retry check
If non-enforcing mode:
- probe may still run for diagnostics but must not block.
3. In-Session Experience​
3.1 Required Shell Elements​
- persistent top bar:
- session state chip (
in_progress,syncing,submitted,closed) - timer (when
timeLimitOverridepresent) - connectivity badge (online/offline/reconnecting)
- session state chip (
- item navigator:
- index/position
- answered/unanswered marker
- answer panel:
- question content
- response controls
- action rail:
Save response(implicit on interaction)Review and submit
3.2 Answer Write Contract​
Writes use:
POST /evaluations/\{id\}/sessions/\{sessionId\}/answers- one
Idempotency-Keyper logical answer write
Payload fields used by UX:
questionVersionIditemPosanswer- optional timing/context (
startedAt,answeredAt,timeSpentMs,currentSectionId,maxViewedItemIndex)
UX behavior:
- optimistic local update is allowed
- row/item state must reconcile with API response
- duplicate clicks must not create duplicate writes (idempotency reuse)
3.3 Session Events (Telemetry)​
Best-effort runtime telemetry:
POST /evaluations/\{id\}/sessions/\{sessionId\}/events- no idempotency key required by policy
Use for:
- focus lost/regained
- navigation markers
- reconnect events
Telemetry failure must not block answering.
3.4 Candidate Accessibility Behaviors (MUST)​
- Question prompts, choices, and response controls must expose clear programmatic labels.
- Item navigation must support sequential keyboard traversal and explicit "current position" context.
- Timer and expiry warnings must be announced through accessible live-region behavior.
- Submit confirmation must be a distinct, accessible step (clear action, clear consequence).
- Error summaries must move focus predictably to the relevant recovery control.
4. Submit Flow​
4.1 Submit Contract​
- endpoint:
POST /evaluations/\{id\}/sessions/\{sessionId\}/submit - use
Idempotency-Key(reuse stable key for retries) - optional
langfor localized snapshot projection
Note:
- session DTO includes
submitIdempotencyKey; use it as default submit key seed.
4.2 Verification/High-Rigor Submit​
Submit payload can include verification.level and verification.context.
If verification.level == 4:
- backend enforces
sessions.proctorcapability - UX must surface explicit "additional verification/authorization required" instead of generic failure text
4.3 Completion Outcomes​
On 201:
- show completion confirmation
- persist returned
submissionId - route to completion screen with next actions:
View attemptReturn to assignments(or issuer-defined destination)
If submit returns error after user has answered:
- keep local answers intact
- show retry-safe message based on idempotency/error code
5. Result and Attempt Review UX​
Primary read:
GET /submissions/\{id\}?view=candidate
Behavior:
- candidate-safe result projection depends on
feedbackMode:items: show item-level answer detailtags: show submission summary + feedback tagsnone: hide score/outcome feedback fields
For forensic/explain surfaces (operator/admin):
- use session attempt report endpoints:
GET /session-attempts/{submissionId}GET /session-attempts/{submissionId}/export
6. Runtime State Model​
6.1 Candidate-Facing States​
launch_readypreflight_checkin_progresssyncingsubmit_pendingsubmittedclosed_expiredclosed_terminatederror_recoverable
6.2 Transition Rules (MUST)​
- never transition to
submitteduntil submit response is acknowledged closed_*states are terminal in runtime UI- reconnect returns to
in_progressonly after next successful write or probe
7. Offline-Forward UX Contract (Target-State)​
This section defines how to design now so offline mode lands cleanly later.
7.1 Local Attempt Journal​
Client keeps an append-only local journal per session:
answer_intents(ordered)event_intents(best-effort ordered)- submit intent marker
Each durable intent stores:
- local sequence number
- payload
- generated idempotency key
- client timestamp (forensics only)
- sync state (
pending,sent,acked,rejected)
7.2 Sync Semantics​
When connectivity returns:
- flush pending
answer_intentsin sequence - flush
event_intentsbest-effort - submit only after answer intents are acked
Server truth rules:
- expiry/time windows still enforced by server clock
- offline client clocks do not override expiry
- rejected intents are shown with concrete reason and next action
7.3 UX Requirements for Offline Mode​
- persistent offline badge while disconnected
- explicit unsynced count
Last syncedtimestamp- non-blocking answer entry while offline (within policy)
- clear terminal message if session expires before sync completes
7.4 Backend Gaps to Plan (Tracked)​
To fully support robust offline sync, backend should add:
- session state hydration endpoint (authoritative resume snapshot with accepted positions/status/expiry)
- batch answer ingest endpoint for efficient replay
- sync acknowledgement shape (accepted/rejected intent details)
7.5 Proposed Offline Sync API Contract (Target-State, Not Live)​
The frontend should be designed against this proposed shape so offline UX can be activated without a navigation rewrite.
Proposed reads:
GET /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/sync/state- returns authoritative server session state:
- session status
- expiry and timing boundaries
- highest accepted position/index
- last accepted intent sequence
- returns authoritative server session state:
Proposed writes:
-
POST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/sync/intents:batch- accepts ordered
answer_intentsandevent_intents - returns per-intent ack:
acceptedduplicaterejectedwith machine reason
- accepts ordered
-
POST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/sync/submit- idempotent submit confirmation once answer intents are fully reconciled
UX safety rules for future activation:
- never show
submittedlocally until server submit ack is received. - expired/terminated server state hard-stops local replay.
- rejected intent reasons map to user actions:
stale_session: reopen/exitpolicy_violation: review and fixpayload_invalid: retry with corrected data
8. Error Contract for Runtime UX​
Status handling:
400: validation/policy issue (invalid payload, lockdown requirement not satisfied)401: authentication required/expired403: capability/policy blocked404: scope/resource not found409: idempotency conflict or request in progress500: retryable system failure (show safe retry UX)
Runtime guidance:
- never discard unsent answers on transport or 5xx failures
- treat idempotency conflicts as recoverable with deterministic messaging
- for policy failures (expiry/lockdown), move to explicit terminal or remediation state
9. Operations Handoff Contract​
Delivery UX and Command Centre must agree on shared primitives:
- status taxonomy
- run label
- progress ratio/current item index semantics
- telemetry event naming
Assignment monitor label mapping (shared with Operations/Language contract):
- backend
invited|started|completed|expired-> UINot started|In progress|Completed|Expired
After submission or close:
- assignment monitor (
/assignments/\{id\}/monitor) becomes the live-to-history bridge - submission and session-attempt report become durable analysis surfaces
10. Definition of Done (Runtime UX)​
- Launch, preflight, in-session, submit, and completion screens are defined with explicit API bindings.
- Lockdown-required flows block safely with remediation copy and retry action.
- Answer and submit actions use idempotency-safe UX behavior.
- Candidate result rendering respects feedback mode contract.
- Runtime state model includes recoverable connectivity and terminal closure states.
- Offline-forward UX constraints are specified without violating current server-authoritative timing rules.
- Operations handoff fields are explicitly aligned for monitor + forensic views.
- Candidate-facing completion path (start -> answer -> submit) is verified on phone, iPad, keyboard-only, and screen-reader-assisted flows.
- Timer/expiry/lockdown/submit-risk signals are accessible, plain-language, and non-ambiguous.
- Runtime accessibility and resilience checks are treated as release gates, not optional QA.