# 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](commit.md) — using the commit API from Python - [Errors](errors.md) — `dsviper.Error` and exception handling