Skip to main content

Sync Contract

:::info Source Sourced from services/assessment-service/SYNC_CONTRACT.md in the documentation repo. :::

1. Applicability

Quiz banks, branching scenarios, and attempt results are replicable for offline delivery.

2. Sync Registration

{ service: 'assessment', entityType: 'QuizBank', conflictPolicy: 'server_authoritative', deltaProjector: 'quiz_banks_by_course_version', pushHandler: 'none', versionField: 'updatedAt', schemaRef: 'schemas://assessment/quiz_bank/v1' }
{ service: 'assessment', entityType: 'BranchingScenario', conflictPolicy: 'server_authoritative', deltaProjector: 'branching_scenarios_by_course_version', pushHandler: 'none', versionField: 'updatedAt', schemaRef: 'schemas://assessment/branching_scenario/v1' }
{ service: 'assessment', entityType: 'AttemptResult', conflictPolicy: 'append_only', deltaProjector: 'results_by_attempt', pushHandler: 'ScoreResponses', versionField: 'scoredAt', schemaRef: 'schemas://assessment/attempt_result/v1' }

3. Offline Behavior

  • Quiz banks + branching scenarios shipped inside PlayPackage bundle (encrypted).
  • Answer keys encrypted with per-bundle key; player decrypts only to score locally.
  • Scoring happens locally; AttemptResult queued in statements-outbox.
  • Randomization seed derived from (attemptId, questionPoolId); reproducible offline + online.

4. Delta Format

{
"cursor": { "lamport": 42, "scope": "assessment:course_version:cv_01H..." },
"upserts": {
"QuizBank": [ ... ],
"BranchingScenario": [ ... ]
},
"deletes": { }
}

5. Conflict Resolution

  • QuizBank / Scenario: server_authoritative. Device receives latest version; no client-side edits.
  • AttemptResult: append_only. Each attempt has unique ID; no conflicts.

6. Cursor Rules

  • Scope: assessment:course_version:{cvId} (quiz banks cascade to bundles).
  • Cursor advances when QuizBank updates (new question, grading rule change).

7. LocalStore Schema

quiz_banks: (id PK, courseVersionId, payload (encrypted), version, updatedAt)
branching_scenarios: (id PK, courseVersionId, payload (encrypted), version)
attempt_results: (attemptId PK, clientMutationId, responses, scaledScore, passed, durationSeconds, scoredAt)

8. Push Flow

Client scores locally → appends AttemptResult.
Sync pushes: POST /sync/v1/push with AttemptResult mutation.
Server validates → forwards to assessment-service.
assessment-service verifies signature (derived from bundle key), persists, emits assessment.attempt_result.scored.v1.

9. Security

  • Answer keys never leave bundle in plaintext.
  • Scoring proof: SHA256(responses + seed + timestamp) included in AttemptResult for audit.
  • Tamper with bundle → can't decrypt answer key → fails scoring → refuses.