Skip to main content

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/redeem
    • POST /api/v1/evaluations/\{id\}/sessions
    • POST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/answers
    • POST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/submit
    • POST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/events
    • GET /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/lockdown/probe
  • Post-attempt forensic views:
    • GET /api/v1/submissions/\{id\} (supports view=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)​

  1. Candidate confidence first: always show "what state am I in" (ready, in progress, syncing, submitted, closed).
  2. Server is authoritative for timing and final status.
  3. Local saves are useful, but not equivalent to durable submit.
  4. High-stakes checks must be visible in plain language, not hidden in technical errors.
  5. 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:

  1. Launch (redeem + preflight)
  2. In Session (answering + navigation + sync state)
  3. Submit Review (final check + submit action)
  4. Completion (submitted/closed outcome)
  5. 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/redeem with token
  • response provides:
    • session
    • session bearer token
    • securityConfig

Mode B: internal/managed launch

  • call POST /evaluations/\{id\}/sessions
  • include userId (currently required by backend), optional versionId, 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/probe before 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 timeLimitOverride present)
    • connectivity badge (online/offline/reconnecting)
  • 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-Key per logical answer write

Payload fields used by UX:

  • questionVersionId
  • itemPos
  • answer
  • 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 lang for 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.proctor capability
  • 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 attempt
    • Return 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 detail
    • tags: show submission summary + feedback tags
    • none: 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​

  1. launch_ready
  2. preflight_check
  3. in_progress
  4. syncing
  5. submit_pending
  6. submitted
  7. closed_expired
  8. closed_terminated
  9. error_recoverable

6.2 Transition Rules (MUST)​

  • never transition to submitted until submit response is acknowledged
  • closed_* states are terminal in runtime UI
  • reconnect returns to in_progress only 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:

  1. flush pending answer_intents in sequence
  2. flush event_intents best-effort
  3. 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 synced timestamp
  • 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:

  1. session state hydration endpoint (authoritative resume snapshot with accepted positions/status/expiry)
  2. batch answer ingest endpoint for efficient replay
  3. 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

Proposed writes:

  • POST /api/v1/evaluations/\{id\}/sessions/\{sessionId\}/sync/intents:batch

    • accepts ordered answer_intents and event_intents
    • returns per-intent ack:
      • accepted
      • duplicate
      • rejected with machine reason
  • 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 submitted locally until server submit ack is received.
  • expired/terminated server state hard-stops local replay.
  • rejected intent reasons map to user actions:
    • stale_session: reopen/exit
    • policy_violation: review and fix
    • payload_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/expired
  • 403: capability/policy blocked
  • 404: scope/resource not found
  • 409: idempotency conflict or request in progress
  • 500: 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 -> UI Not 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)​

  1. Launch, preflight, in-session, submit, and completion screens are defined with explicit API bindings.
  2. Lockdown-required flows block safely with remediation copy and retry action.
  3. Answer and submit actions use idempotency-safe UX behavior.
  4. Candidate result rendering respects feedback mode contract.
  5. Runtime state model includes recoverable connectivity and terminal closure states.
  6. Offline-forward UX constraints are specified without violating current server-authoritative timing rules.
  7. Operations handoff fields are explicitly aligned for monitor + forensic views.
  8. Candidate-facing completion path (start -> answer -> submit) is verified on phone, iPad, keyboard-only, and screen-reader-assisted flows.
  9. Timer/expiry/lockdown/submit-risk signals are accessible, plain-language, and non-ambiguous.
  10. Runtime accessibility and resilience checks are treated as release gates, not optional QA.