Commit

Viper's subsystem for the mutation DAG — versioned history, undo/redo, time travel.

The three layers

Commit is structured as three layers, from the disk upward. Each has a precise role; together they form the substrate of every Commit Application.

DSM
shape
CommitDatabase
persistence
CommitStore
runtime surface
Application Context
application pattern

CommitDatabase

the persistence layer

  • Typed, content-addressed persistence — immutable mutation DAG
  • Read at any past commit, never an implicit "current"
  • Typed mutations as transactions
  • Deterministic reduction for combining heads
Read the manual ↗

CommitStore

the runtime surface — holds the current state

  • Holds the current state of an open CommitDatabase
  • Dispatch surface — typed actions, each producing a transactional commit
  • Undo / redo, multi-head exploration
  • Notifier for observers (framework-agnostic)
Read the manual ↗

Commit Application Model

the application pattern

  • An Application Context composes a CommitStore with typed code generated by Kibo from your DSM and domain logic
  • Exposes the dispatch surface to the UI
  • Two language profiles: C++ with function pools, Python without
  • The only layer the application developer writes
Read the manual ↗

Without the Commit Database, reads and writes go against the plain Database backend — flat key-value, no history. The Commit Database adds versioning on top : an immutable mutation DAG with deterministic reduction. The CommitStore adds the runtime surface (dispatch, undo, notifications). The Commit Application Model is how a real application composes them together.

Commit Application Model — two language profiles

Same Model, two realities. Python collapses one layer because there is no language boundary to cross.

C++ profile

6 layers — function pools cross to Python

1
UI Layer Platform UIs — AppKit · Qt Widgets · Qt Quick / QML
2
Application Context Owns the store, domain state, dispatch — plus the generated function pools
3
Bridge Layer Pool bridges route generated pool calls into hand-written business logic
C++ only
4
Business Logic (C++) Hand-written domain code, invoked through the bridge
5
Generated by Kibo Data classes, attachments, definitions, function pools
6
Viper C++ Runtime Type · Value · Database · Commit · Serialization · RPC

Python profile

5 layers — no boundary, no pools

1
UI Layer Platform UIs — Qt Widgets · Qt Quick / QML · server-rendered HTML/CSS
2
Application Context Owns the store, domain state, dispatch
3
Business Logic (Python) Hand-written domain code, invoked directly by dispatch
4
Generated by Kibo Data classes, attachments, definitions
5
dsviper Runtime Type · Value · Database · Commit · Serialization · RPC

The differentiator is the Bridge Layer. In C++, generated function pools (held by the Context) and hand-written pool bridges expose typed business code to Python scripting. In Python, the dispatch lambda calls business functions directly — same layer, no pool.

UI
Application Context (matches home stack)
Bridge (C++ only)
Business Logic
Generated by Kibo (matches home stack)
Foundation / Runtime (matches home stack)

Raptor Editor is the example par excellence of this model : two completely independent native UI shells — an AppKit shell on macOS (~6,100 lines) and a UIKit shell on iPadOS (~5,500 lines) — over the same bridges, the same business logic, the same generated data classes, the same Viper C++ runtime. Each shell is roughly 3 % of the project ; the second one cost as much as the first, with no shared code between them. Adding a Service is one more layer-1 surface on that same core, not a second code base.

How you use the mutation DAG

Two independent axes : the operations are always available ; the context decides whether the engine has anything to pick between. The Dual-Layer Contract applies along the context axis, not the operation axis.

What you can do — operation axis, always available

Time travel

read-only

Pick any past commit and read the world as it was at that point. Determinism, immutability, content addressing — the same state, every time.

Undo / redo

append-only

Step back along the chain, diverge, redo. Implemented by appending an Enable / Disable commit that masks a target — never rewriting history.

Multi-head exploration

parallel heads

Diverge the DAG and keep parallel heads alive ; merge them back on your own schedule.

Where you do it — context axis, decides whether the Contract applies

Single-stream

one author at a time

Linear history. Every read returns exactly what you wrote — nothing for the engine to pick between. The Contract stays on the shelf.

Multi-stream — disjoint by design

scope-decomposed cooperation

Concurrent writers operate on structurally disjoint targets ; the engine still has nothing to pick between — every intent survives intact. The Contract is reference material.

Multi-stream — strong invariants

uniqueness, referential integrity, regulated domains

Concurrent writers may collide on overlapping paths. The engine picks deterministically but cannot preserve every intent. This is where the Contract is load-bearing — exit by re-architecting upstream toward disjoint scope, or by supervising reduction.

Operations × context : doing an undo in single-stream is trivial. The same undo in multi-stream reads through a state produced by an earlier reduction — the read crosses an import boundary. The Modes of Use diagnostic names this distinction.

Sharing a CommitDatabase across machines

A CommitDatabase is a SQLite file. Multiple processes on the same machine open it directly through SQLite's own concurrent access — no server needed. The two patterns below address the cross-site case : how a CommitDatabase reaches more than one machine.

Transparent proxy

remote, no local copy

A thin RPC proxy talks to a server that holds the SQLite file. The application calls a CommitDatabase the same way as a local file ; it does not know whether the file is on this machine or behind a network hop.

Useful for centralised editing of a single shared database.

Replicated

local copy + periodic sync

Each site holds its own local CommitDatabase ; a synchroniser copies commits between any two instances (local↔local, local↔remote, remote↔remote).

Offline work, local-first reads, bandwidth amortisation.

Each site has its own write head, so a replicated topology is multi-stream by construction — the diagnostic above applies.

Sync copies commits ; it does not merge them. After sync, both sides hold the union of commits — including any divergent heads. Reducing them to a single head is a separate step, left to the application. Read the sync chapter ↗

The Dual-Layer Contract

When invariants are strong — uniqueness, referential integrity, regulated domains — the Commit Database is deliberately domain-agnostic. It never refuses to reduce ; it picks a deterministic outcome and moves on. Validation moves to the application, at read time, not at write time.

Commit Database guarantees

  • Deterministic reduction — same inputs in the same order always produce the same output.
  • DAG consistency — commits form a valid directed acyclic graph.
  • Immutability — once committed, data cannot be modified.
  • Content-addressingCommitId = SHA-1(content), tamper-evident.

The application provides

  • Validates on read. Treats post-reduction state as an import boundary.
  • Enforces semantic integrity. Uniqueness, referential integrity, cross-field invariants.
  • Assumes some mutations may have been dropped. The engine silently ignores mutations whose targets disappeared — no alert.
  • Copes with LWW outcomes. The engine may keep a value that no single intent would have written.

“I guarantee structural integrity. I hand you back data that is structurally sound but semantically untrusted. You re-validate it on read.”

— The Dual-Layer Contract

Reference implementations

Shipped with DevKit is cdbe.py, a generic editor without a domain. The other three are illustrative walk-throughs — each wires the full chain in real code, on a different presentation tier (Qt Widgets, Qt Quick / QML, server-rendered HTML/CSS), to show how the pieces fit together.

shipped

cdbe.py

Generic Commit Database Editor — Qt Widgets

Opens any CommitDatabase through the dynamic API and exposes the full mutation DAG : history, undo/redo, synchronisation with a remote server, embedded Python scripting. A minimal Application Context without a domain — the working tool, not a walk-through.

Illustrative walk-throughs

ge-py

Graph Editor — Qt Widgets desktop application

PySide6 desktop application demonstrating dsviper end-to-end with a graph database visualization. Built on dsviper-components (Qt Widgets).

ge-qml

Graph Editor — Qt Quick / QML port

QML port of ge-py, built on dsviper-components-qml (Qt Quick). Same value chain, same business logic, same CommitStore facade — the UI is QML driven by Python QObject models registered as QML context properties.

web-cdbe

Server-rendered HTML/CSS

Third presentation-tier instance — Flask + HTML5, no JavaScript. Same dsviper foundation as cdbe, surfaced through a server-rendered web UI instead of a Qt one. Illustrates that the chain is not bound to a specific UI toolkit.