Skip to main content

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

  1. The Ledger Table: Stores every event.
    • Naming Convention: <entity>_history or <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.
  2. The Pointer: The parent entity stores a pointer (e.g., latest_score_version_id) to the current authoritative row.
  3. 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 mutable assignment_overrides table should migrate to this pattern or add strict audit logging).
    • Credit Consumption: billing_ledger (Standardised name).
  • 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.