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;
AttemptResultqueued 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.