ADR-0010: The Ledger Pattern for Mutable State
Status
Proposed
Context
In a KOE (Knowledge, Observation, Evidence) system, high-stakes data changes often require an audit trail. Simple CRUD (overwriting a value) destroys the history of why a change happened. This is unacceptable for disputes, compliance, and financial tracking.
Decision
We will apply the Ledger Pattern (Immutable History + Current Pointer) for all high-stakes state changes.
The Pattern
- The Ledger Table: Stores every event.
- Naming Convention:
<entity>_historyor<entity>_versions. - Columns:
id,tenant_id,org_unit_id,entity_id,actor_id,value,reason,created_at. - RLS: Must include standard RLS policies and
(tenant_id, org_unit_id)indexing. - Indices:
(entity_id, created_at DESC)or(entity_id, version_id DESC)for fast retrieval. - Constraint: Rows are INSERT ONLY.
- Naming Convention:
- The Pointer: The parent entity stores a pointer (e.g.,
latest_score_version_id) to the current authoritative row. - Atomicity: Updates to the pointer MUST occur in the same transaction as the ledger insert, adhering to the canonical lock ordering to prevent race conditions.
Scope of Application
- MUST USE for High-Stakes Data:
- Results Remediation:
submission_score_versions(Already implemented as the precedent for this pattern). - Human Marking:
item_score_history(Assessor vs. Moderator traces). - Certificate Status:
certificate_status_history(Issued → Revoked). - Assignment Overrides:
assignment_override_history(Note: Current mutableassignment_overridestable should migrate to this pattern or add strict audit logging). - Credit Consumption:
billing_ledger(Standardised name).
- Results Remediation:
- DO NOT USE for Low-Stakes Data:
- UI preferences, user profile bios, draft content descriptions. (Standard CRUD is sufficient).
Consequences
- Auditability: We can reconstruct the exact state of the system at any point in time.
- Performance: Reads must use the stored Pointer (
latest_id) rather than scanning the history table. - Consistency: Aligns new features with the existing Remediation architecture.