gabriel / muse public
op-log.md markdown
90 lines 3.2 KB
Raw
sha256:660fcac1df3ab28f61862e961890bd2ca8b754fa0242079d93ca1e25037ec8a6 chore(tests): add docstring to tests/__init__.py so rc14 tr… Human 26 days ago

Op Log: Append-Only Operation Log

Purpose

The op log is the staging area between real-time collaborative edits and the immutable Muse commit DAG. During a live session, every operation is appended to the log as it occurs. At commit time, the log is collapsed into a StructuredDelta and stored with the commit record.

Why not just commit frequently?

The commit DAG is optimised for immutability and verifiability, not for sub-second edit throughput. A live AI agent producing 100 note edits per second cannot commit 100 times per second — the overhead of hashing, writing to the object store, and updating refs would dominate.

The op log is optimised for append throughput: it is a flat JSON-lines file with no locking beyond OS-level file appends. A checkpoint converts the accumulated ops into a single commit.

Structure

.muse/op_log/<session_id>/
    ops.jsonl        — one JSON line per OpEntry (append-only)
    checkpoint.json  — most recent checkpoint record

OpEntry fields

Field Description
op_id UUID4 — stable identifier for this operation
actor_id Agent or human identity
lamport_ts Logical Lamport timestamp for causal ordering
parent_op_ids Causal dependencies (empty = root entry)
domain Domain tag ("music", "code", …)
domain_op The typed domain operation
created_at ISO 8601 wall-clock timestamp (informational)
intent_id Coordination intent linkage (empty if none)
reservation_id Coordination reservation linkage (empty if none)

Lamport timestamps

Lamport timestamps provide total ordering across concurrent actors without wall-clock coordination. Each actor maintains a counter; every new entry increments it. When two actors merge their logs, the resulting Lamport clock continues from max(a.lamport, b.lamport) + 1.

The OpLog class initialises its counter from the highest value found in the log file on first access, so that a reopened session continues correctly.

Checkpoints

A checkpoint marks the point where all ops up to a given Lamport timestamp have been crystallised into a Muse commit:

ckpt = log.checkpoint(snapshot_id="snap-abc123")

After a checkpoint, replay_since_checkpoint() returns only ops that arrived after the checkpoint — enabling incremental application without re-reading the full log.

The log file itself is never truncated. Compaction (deleting old log files) is a separate archival operation outside the scope of this module.

Lifecycle

live edits → OpLog.append()           → ops.jsonl
session end → OpLog.to_structured_delta() → StructuredDelta
commit      → OpLog.checkpoint(snap)  → checkpoint.json
                                      → normal Muse commit DAG

Domain neutrality

The op log stores DomainOp values unchanged. The core engine has no opinion about what those ops mean. Each domain plugin collapses its own slice using OpLog.to_structured_delta(domain).

File Role
muse/core/op_log.py OpEntry, OpLogCheckpoint, OpLog, list_sessions
muse/domain.py DomainOp, StructuredDelta
tests/test_op_log.py Unit tests
File History 3 commits
sha256:660fcac1df3ab28f61862e961890bd2ca8b754fa0242079d93ca1e25037ec8a6 chore(tests): add docstring to tests/__init__.py so rc14 tr… Human 26 days ago
sha256:d8316ffae901be06347e16ab55be11868eb519dd16ade3e8aa16a99e662f7e62 baseline: rc14 re-baseline after rc3 store corruption recovery Human patch 26 days ago
sha256:1c4b3e3a9a1f300774c3ee662b572a698d5fd405bf765a71e6011a2e9c3eaaaa feat: Muse — version control for the agent era Human 100 days ago