CRISPcore

Principle

Immutability

CRISP rejects silent overwrite of Conform history. Corrections are new versions. How you retire the prior version — interval close (UPDATE ends) vs stricter insert-only tombstones — is an implementor choice. Both stay compatible with tritemporal axes and with Span/Publish as-of reads.

What kind of immutability

The baseline is history-preserving immutability: keep past belief queryable; apply corrections as new versions; allow controlled incremental MERGE only where it preserves history semantics. It is not “never update a cell platform-wide” as a forced default — and it is not soft-delete flags without temporal axes.

Correction on a timeline

At t0 the platform stores limit 100. At t1 a correction arrives: limit 120. What can you still read afterward?

Overwrite-only

t0t1 correctionnow
100
120

After t1 the store holds only 120. Ask “what did we believe between t0 and t1?” — no row left to answer.

Out of bounds for regulated Conform paths

Versioned history

t0t1 correctionnow
100
120

Prior interval stays: 100 for [t0, t1), 120 from t1. As-of reads still work.

CRISP baseline — history-preserving

Same correction (limit 100 → 120). Overwrite leaves only the latest value; versioned history keeps both intervals so as-of belief stays queryable.

Two compatible modes

Both modes use the same tritemporal columns (valid_*, system_*, warehouse_*). They differ in whether the load may UPDATE interval ends on a prior row.

ModeOn correctionTypical read of “current”
Interval closedefault / commonUPDATE prior system_to / warehouse_to, then INSERT successorOpen interval — e.g. system_to IS NULL
Strict tombstoninginsert-onlyNever mutate prior rows; INSERT a close/tombstone marker, then INSERT successorTombstone-aware chain (Span encapsulates the logic)

You may run one mode platform-wide, or a mixed policy by domain. What CRISP forbids is treating the sole Conform copy as a mutable current row with no recoverable history.

Two CRISP-compatible modes

Interval close

Default / common

Prior open · system_to NULL
↓ UPDATE ends
Prior closed · system_to = T
↓ INSERT
Successor assertion

Strict tombstone

Insert-only

Prior row · bytes never change
↓ INSERT
TOMBSTONE_CLOSE → prior PK
↓ INSERT
Successor ASSERTION
Both modes keep tritemporal semantics. They differ only in whether prior row bytes may be updated on close.
Interval close (default pattern)
# Prior belief stays queryable; only ends are stamped
UPDATE SP_CORE_CONFORM.{entity}
  SET system_to = :close_ts,
      warehouse_to = :load_ts
  WHERE assertion_pk = :prior AND system_to IS NULL;

INSERT INTO SP_CORE_CONFORM.{entity}
  (... successor columns, system_from = :close_ts ...);
Strict tombstone (no UPDATE on prior rows)
# Prior row bytes never change
INSERT INTO SP_CORE_CONFORM.{entity}
  (record_kind, supersedes_assertion_id, ...)  -- TOMBSTONE_CLOSE → prior PK

INSERT INTO SP_CORE_CONFORM.{entity}
  (record_kind, supersedes_assertion_id, ...)  -- ASSERTION successor → prior PK

How this hangs off tritemporal

Interval-close mode stamps ends on the prior row. Strict mode encodes the same close event as an appended marker so physical rows stay insert-only. Span and Publish as-of contracts stay the same; only the load and “current open” helpers change.

What is out of bounds

Upgrade-safe column contract

Deploy optional chain columns from day one (record_kind, supersedes_assertion_id) even if you start in interval-close mode. Switching to strict tombstoning then becomes a load + Span register change — not a DDL migration of assertion tables.

Checklist

  1. Never define Conform truth by silent overwrite.
  2. Pick interval-close and/or strict tombstone per domain; document it.
  3. Keep loads idempotent so reruns do not double-apply business effect.
  4. Stamp warehouse_* only in the trusted Conform load.
  5. Expose as-of through Span/Publish — not per-mart rewrite rules.