Event Log System
Status: Implemented (event log + bookmarks live; history collapse scheduled via background task) Reference epics: INK-834 Crate: crates/infrastructure/sqlite/src/workspace/event_log_repository.rs Migration: V001 baseline (event_log, bookmarks, event_log_summaries)
Overview
Section titled “Overview”The event log is a continuous, immutable record of every structural mutation in the workspace — page creations, tag assignments, property changes, lifecycle transitions, derivation links added, and so on. Paired with Loro CRDT operation history for text content, it gives the workspace fine-grained point-in-time reconstruction without the cost of full database snapshots.
Three forces shape the design:
- Cloud sync makes local snapshots redundant. Supabase sync already replicates workspace state; CRDT operation logs already replay content. Full SQLite snapshots duplicate both.
- Event data is the World Agent’s workspace-temporal primitive. Scheduled autonomous tasks need to reason about “what changed” in categorically meaningful units — not by scanning database rows. The event log is that stream.
- Named bookmarks are more natural than snapshots. Authors think in milestones (“Chapter 3 Draft”), not database copies. Bookmarks are lightweight anchors in the event timeline.
Three pillars of history
Section titled “Three pillars of history”1. CRDT content history
Section titled “1. CRDT content history”Loro CRDT operation logs capture every text edit at the operation level. Any block’s content at any past timestamp is reconstructable by replaying its CRDT ops. This pillar requires no SQLite-side machinery.
2. Structural event log
Section titled “2. Structural event log”A continuous, append-only log of all non-content mutations. Each event records:
- What changed — entity type + entity ID + change type
- When — ISO 8601 timestamp with sub-second precision
- Before/after — JSON snapshots of the mutated fields
- Attribution —
device_id(for user/device-originated writes) or agent identity (for agent writes) - Originating source — whether the event came from an interactive session, a scheduled autonomous task, or an import (see §Event source and autonomous-task distinguishability)
Event types
Section titled “Event types”| Entity | Event types |
|---|---|
| Page | created, moved, renamed, deleted, restored, type_changed |
| Block | created, deleted, reordered, type_changed, area_assigned |
| Tag | created, renamed, merged, deleted |
| Page–Tag | assigned, removed |
| Attachment | added, removed, replaced |
| Property | set, changed, removed |
| Layout | applied, removed, changed |
| Reference | created, removed (backlink tracking) |
| Lifecycle | transitioned (e.g. Draft → Candidate, Candidate → Canonical; see provenance §lifecycle) |
| Derivation | link_added, link_removed (see derivation-links) |
| Metadata | field_changed (catch-all for any entity field mutation) |
Lifecycle transitions and derivation changes are first-class events so that the submit-boundary and re-validation surfaces can be reconstructed from the log alone.
Schema
Section titled “Schema”CREATE TABLE event_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, entity_type TEXT NOT NULL, -- 'page', 'block', 'tag', 'attachment', etc. entity_id TEXT NOT NULL, -- UUID of the affected entity event_type TEXT NOT NULL, -- 'created', 'moved', 'renamed', etc. before_value TEXT, -- JSON: previous state (NULL for 'created') after_value TEXT, -- JSON: new state (NULL for 'deleted') device_id TEXT, -- Attribution for device/user-originated writes event_source TEXT NOT NULL, -- 'interactive' | 'autonomous_task' | 'import' | 'sync' source_ref TEXT, -- Task category (for autonomous_task), import batch id, etc. thread_id TEXT, -- Agent thread id when event_source indicates agent activity timestamp TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')));
CREATE INDEX idx_event_log_entity ON event_log(entity_type, entity_id, timestamp);CREATE INDEX idx_event_log_timestamp ON event_log(timestamp);CREATE INDEX idx_event_log_type ON event_log(event_type, timestamp);CREATE INDEX idx_event_log_source ON event_log(event_source, timestamp);Events are recorded at the application layer during use-case execution, coordinated through WriteEffectCoordinator alongside the sync queue and embedding queue. Every WorldWrite that commits produces one or more event-log entries — the log is downstream of the submit boundary, not parallel to it.
3. Named bookmarks
Section titled “3. Named bookmarks”User-created (or agent-created) markers in the event timeline. A bookmark is not a database copy — it is a lightweight anchor.
CREATE TABLE bookmarks ( id TEXT PRIMARY KEY, -- UUID name TEXT NOT NULL, -- User-visible name ("Chapter 3 Draft") description TEXT, -- Optional context timestamp TEXT NOT NULL, -- The point in the event log this bookmark marks created_by TEXT, -- User id or agent id created_at TEXT NOT NULL);“Restore to bookmark X” means: identify the bookmark’s timestamp, replay the structural event log up to that timestamp, and load CRDT snapshots at that timestamp for content.
Event source and autonomous-task distinguishability
Section titled “Event source and autonomous-task distinguishability”Every event carries an event_source tag. This is the surface that makes the event log work as both an author-facing activity record and the World Agent’s view of what happened in the workspace.
event_source | Meaning | source_ref | thread_id |
|---|---|---|---|
interactive | User edit, or agent write from an in-session conversation turn | Input method (editor / chat / etc.) | Set when the write originated from an agent turn |
autonomous_task | Write produced by a scheduled autonomous task run | Task category (see below) | The task’s resumed thread id |
import | Write produced by an import (bulk or single) crossing the submit boundary | Import batch id | null |
sync | Write arriving via Supabase sync from another device | Source device id | null |
Autonomous-task categories (per scheduling-system): structural_maintenance, revalidation_processing, content_proposals, external_imports, canonical_modifications. The category is the primary filter the History Panel and the agent’s own “what did I do while you were away?” views key on.
This distinction is first-class because the three audiences for the event log need different slices:
- The author wants to see “what did I do today?” (interactive only) and “what did the agent do while I was away?” (autonomous_task only) as separate, filterable views — not a single chronological blur. Per parking-lot PL-B, autonomous-task activity does not get a separate feed; it gets filterable status in the event log.
- The World Agent reasons over interactive and autonomous-task events differently: its own past autonomous actions carry
origin: AgentProducedand are its own record, whereas interactive user edits are the signal that the workspace moved without agent involvement. - Re-validation (retroactive-revision) cares whether a write was interactive (likely deliberate, high-weight for re-validation) or from sync (already reconciled elsewhere, low signal).
Denials from the permission system are not event-log entries. The capability system records its own denial trail; a permission denial did not mutate the workspace and does not belong in a mutation log. This aligns with domain rule 7 — capability-denied is not deviation.
Retention and history collapse
Section titled “Retention and history collapse”Within retention window
Section titled “Within retention window”Full per-mutation granularity is preserved. Every individual write is available for pattern discovery, author inspection, and fine-grained reconstruction. Default: 90 days (configurable via event_log_retention_days).
Beyond retention window
Section titled “Beyond retention window”History between named bookmarks collapses into a single summary entry per entity per bookmark interval. Collapse is performed by the Background task runner on a scheduled cadence. Summaries are stored in event_log_summaries (V001 baseline) and preserve the event_source distribution across the collapsed interval so filtered views survive collapse.
Named bookmarks are never pruned — they are the author’s explicit markers.
Tauri commands
Section titled “Tauri commands”query_page_events— events for a specific pagequery_page_timeline— page timeline with CRDT content events mergedquery_timeline— workspace-wide timelinequery_timeline_filtered— timeline filtered byevent_source(interactive, autonomous_task category, import, sync)create_bookmark,get_bookmark,list_bookmarks,delete_bookmark— bookmark management
Application layer
Section titled “Application layer”Located in crates/application/src/event_log/ and crates/application/src/bookmark/:
RecordEventUseCase— records mutations as they occur (called byWriteEffectCoordinatorpost-submit-boundary)QueryPageEventsUseCase/QueryTimelineUseCase/QueryPageTimelineUseCase— readsCreateBookmarkUseCase/GetBookmarkUseCase/ListBookmarksUseCase/DeleteBookmarkUseCase— bookmark CRUD
Integration with the World Agent
Section titled “Integration with the World Agent”The event log is the World Agent’s primary workspace-temporal data source:
- Queries like “what changed since the last autonomous-task run on this workspace?” resume from the task’s last event-log cursor rather than rescanning content.
- Context assembly (see agent-memory-system) can pull recent event-log slices as ambient context without having to load full entities.
- Autonomous tasks read their own prior event-log entries (
event_source = 'autonomous_task', matchingsource_ref) to avoid redoing work the previous run of the same category already did. - Bookmarks provide natural session boundaries the agent can refer to in conversation (“since the Chapter 3 Draft bookmark, you added four characters and renamed the capital city”).
The World Agent does not write to the event log directly. It produces WorldWrite values through the submit boundary; the boundary’s adapter layer populates event_source, source_ref, and thread_id from call context, and the post-boundary WriteEffectCoordinator records the resulting events. This keeps the log downstream of every provenance invariant.
Related
Section titled “Related”- Scheduling system — where autonomous-task categories are defined and scope grants live
- Submit boundary — upstream invariant every event record is downstream of
- Task runner system — runs history collapse on a scheduled cadence
- Agent memory system — consumer of event-log slices for ambient context
- ADR-007: Agent Integration via MCP and Sync — sync-side event-log implications
Was this page helpful?
Thanks for your feedback!