Skip to content
Back to top
View this page

# The Dual-Layer Contract

This page documents the contract between the Commit engine (exposed in Python through `dsviper`) and your application code. Read it before relying on commit behavior to validate your data: the engine produces structurally sound but untrusted output, and your application is what turns it into trusted state.

## The Contract

Layer | Guarantees
--- | ---
Commit | Deterministic merge, DAG consistency, immutability
Application | Re-validates engine output before acting on it

When concurrent streams converge, mutations are applied using a best-effort algorithm:

- Mutations targeting non-existent documents or unresolved paths are silently ignored.

- Business rule violations are not detected by the engine.

- LWW arbitration may keep a value that no single submitted intent would have produced.

This is by design. The engine is intentionally agnostic to your domain rules and never refuses a merge — it picks a deterministic outcome and moves on.

## Why the Engine’s Output Is Untrusted Data

This is the load-bearing point of the contract.

“Untrusted” usually means data that crossed a boundary you don’t control — network input, a deserialized file, an external API. The natural reflex is to validate at that boundary and then trust the data internally.

Best-effort merge breaks that reflex. The state returned by `state(commitId)` is itself a boundary, even though the data never left the process:

- Mutations you submitted may have been silently dropped.

- Two disjoint updates, each individually consistent, may have produced a combined state that violates a cross-field invariant.

- LWW may have preserved a value that no submitted commit would have written on its own.

Structurally the data is sound — types check, paths resolve, the DAG is consistent. Semantically, you have no guarantee that the result reflects any single coherent intent. That makes engine output equivalent to untrusted data from the application’s perspective.

## Three Error Families

Because of this, validation in a `dsviper`-based system happens at three distinct places, not one:

Family | Origin | Detected by
--- | --- | ---
Untrusted (external) | I/O, deserialization, type mismatch, malformed path | Engine, fail-fast → `dsviper.Error`
Untrusted (post-merge) | State returned by the engine after convergence | Application, at read time
Invalid (semantic) | Business-rule violation on otherwise sound data | Application, at read time

The first family is what `dsviper.Error` covers. The other two are entirely your responsibility — and they share a remediation: re-validate when you read the state, not when you build the mutations.

## What Commit Provides

Guarantee | Description
--- | ---
DAG Consistency | Commits form a valid directed acyclic graph
Immutability | Once committed, data cannot be modified
Deterministic Merge | Same inputs always produce the same output
Content-Addressable | `CommitId = SHA-1(content)`, tamper-evident

## What Commit Does NOT Provide

Not Provided | Description
--- | ---
Intent Preservation | Deterministic arbitration (LWW), not your intent
Semantic Validation | No business rule checking
Mutation Notification | No alert when mutations are silently ignored
Conflict Detection | No notion of conflict — just deterministic merge

## Implications for `dsviper` Code

The contract shapes how you should write code on top of `dsviper.Commit*`:

- Treat the post-merge state as a boundary. It is the symmetric equivalent of deserialized network input: structurally sound, semantically unverified.

- Do not assume that every operation you build into a `CommitMutableState` will land. After merging concurrent commits, some operations may have been silently dropped because their targets disappeared.

- Validate at the application boundary, which means the engine output. If your domain has invariants (uniqueness, referential integrity, cross-field consistency), enforce them when consuming the state.

## Summary

> “I guarantee structural integrity. I hand you back data that is structurally sound but semantically untrusted. You re-validate it on read. Together, we guarantee data integrity. Separately, neither of us can.”

> — The Dual-Layer Contract

## See Also

- Commit — using the commit API from Python

- Errors — `dsviper.Error` and exception handling