Skip to content

// Engineering - May 18, 2026

Every action, undoable.

How event sourcing turns your project management activity log from a passive audit trail into the mechanism that powers conflict-aware undo.

Why do most project management tools handle undo so badly?

Most project management tools treat every action as a destructive overwrite. When you update a card description, the old text is gone. When you delete a card, it moves to a trash folder (if you are lucky) or vanishes entirely. When you move a card to the wrong column, you have to remember where it was and manually move it back. There is no undo button, no version history, and no way to answer the question "what was this card's state an hour ago?"

This happens because most tools store current state only. The database contains the latest version of every card, column, and board. When you update a card, the old row is overwritten with the new values. The previous state is gone - not archived, not soft-deleted, just overwritten. This is the simplest storage model, and for many applications it is fine. But for a collaborative tool where multiple people modify shared state, it means every mistake is permanent and every change is invisible.

Event sourcing is the architectural pattern that solves this. Instead of storing current state only, you store every state change as an immutable event. The current state is derived by replaying all events from the beginning - or more practically, by maintaining a current-state projection alongside the event log. This article explains how Flux implements event sourcing for its activity log, why it matters for your team, and what it enables beyond basic undo.

What is event sourcing and how does it work?

Event sourcing is a pattern where the application's state is determined by a sequence of immutable events rather than by direct state mutations. Each event records what happened, when it happened, who did it, and what the state was before and after.

In a traditional CRUD application, updating a card title looks like this: UPDATE cards SET title = 'New title' WHERE id = '...'. The old title is gone. In an event-sourced system, the same operation produces an event record:

  • Action: card.updated
  • Entity: card ID
  • User: who made the change
  • Timestamp: when it happened
  • Before state: { title: 'Old title', ... }
  • After state: { title: 'New title', ... }
  • Idempotency key: deduplication token

The card table is also updated (Flux does not use pure event sourcing where the event log is the only source of truth - the current-state tables exist for fast queries). But the event is preserved permanently. This dual-write approach gives you the performance of direct state queries with the auditability of a complete event history.

Events Flux records

Every mutation in Flux produces an activity event. The complete list:

  • Cards: created, updated (title, description, due date, assignees, labels), moved between columns, archived, restored, deleted
  • Columns: created, renamed, reordered, deleted
  • Boards: created, updated (title, settings), deleted, restored
  • Labels: created, updated, deleted
  • Checklists: item created, toggled, deleted
  • Comments: created, edited, deleted
  • Attachments: uploaded, deleted

Each event includes the full before-and-after state snapshot of the affected entity. This is deliberately verbose - storing snapshots uses more disk space than storing diffs, but it makes undo trivially simple and eliminates the complexity of reconstructing state from a chain of deltas.

How does conflict-aware undo work?

Basic undo is straightforward: read the most recent event, apply the before-state snapshot, done. But collaborative tools introduce a problem that single-user undo does not face: other people may have modified the entity between your action and your undo.

Consider this sequence:

  1. Alice deletes a card at 10:00 AM.
  2. Bob (who had the card open in another tab before the deletion) adds a comment at 10:02 AM (this fails because the card is deleted, but in some systems with stale cache, it might appear to succeed).
  3. Alice realizes the deletion was a mistake and clicks undo at 10:05 AM.

Naive undo would restore the card to its pre-deletion state, losing Bob's comment attempt. Conflict-aware undo checks the activity log for any events that affected the same entity after Alice's deletion. If it finds conflicting events, it presents Alice with the conflicts and asks her to confirm before proceeding. This prevents undo from silently overwriting changes made by other team members.

The conflict detection algorithm is simple because the event log is ordered and immutable:

  1. Find the event to undo.
  2. Query all subsequent events that affect the same entity or its children (for board/column deletions, this includes cards within the deleted column).
  3. If conflicting events exist, return them to the client with the undo blocked.
  4. If no conflicts, apply the inverse operation and record a new undo event in the log.

The undo itself is recorded as an event, which means it too is auditable and undoable. You can undo an undo, preserving the full timeline of what happened.

How does event sourcing improve project accountability?

Beyond undo, the event log serves as a complete audit trail for every board. This has practical value for several team scenarios.

Replacing daily standups

The daily standup meeting exists to answer three questions: what did you work on yesterday, what are you working on today, and is anything blocked? The activity log answers the first two automatically. A team lead reviews the log each morning - five-minute scan - and sees every card creation, move, comment, and completion from the previous day, attributed to specific team members. If something looks stuck, they comment on the card directly. No meeting required.

Onboarding new team members

When a new team member joins, they can read the activity history of any card to understand its full context: why it was created, what discussions happened, when it was moved between stages, and what decisions were made along the way. This context is often lost in tools that only store current state.

Post-incident review

After an incident, the activity log provides a timeline of every board action during the incident period. Who created the incident card? When was it escalated (moved to a higher-priority column)? What workarounds were discussed (comments)? When was the fix deployed (card moved to Done)? This timeline is often more useful than scattered Slack messages and memory reconstructions.

Compliance and regulatory requirements

Regulated industries require documentation of who did what and when. The immutable activity log provides this automatically. Every action has a timestamp, a user attribution, and a complete state record. No additional documentation effort is required from the team - the audit trail is a byproduct of normal board usage.

What are the technical trade-offs of event sourcing?

Event sourcing is not free. There are real costs and design decisions to consider.

Storage growth

Storing full before-and-after snapshots for every event uses significantly more disk space than storing only current state. A card with 50 title edits has 50 snapshot pairs in the event log. For most project management workloads, this is manageable - boards are text-heavy, not media-heavy, and storage is cheap. But teams that generate hundreds of thousands of events per month should plan for log compaction or archival.

Write amplification

Every mutation requires writing to both the current-state table and the activity log. This doubles the write load compared to a CRUD-only system. In practice, project management workloads are read-heavy (users view boards far more often than they modify them), so the additional write cost is negligible relative to the read traffic.

Query complexity for undo

Conflict detection requires querying subsequent events for the same entity. This is a straightforward indexed query (entity ID + timestamp range), but it does add latency to the undo operation. Flux keeps this fast by indexing on (board_id, entity_type, entity_id, created_at), which makes conflict queries sub-millisecond.

Eventual consistency considerations

Because Flux uses dual-write (current state + event log) rather than pure event sourcing, there is a theoretical window where the current state and the event log could diverge if a write succeeds on one and fails on the other. Flux handles this with database transactions - both writes happen atomically. The trade-off for simpler reads is worth the transactional write cost.

What else does event sourcing enable?

The activity log is the foundation for several features beyond undo and audit.

  • Cycle time analytics. Because every card movement is recorded with a timestamp, calculating cycle time (how long a card takes from In Progress to Done) is a simple query against the event log. No separate tracking system is needed.
  • Change attribution for AI agents. When an AI agent modifies a card via the MCP server or REST API, the event is attributed to the API key that made the call. Teams can see exactly what their agents did and undo any action.
  • Replay and debugging. If a board ends up in an unexpected state, the event log lets you trace exactly how it got there - every action, in order, with full context. This is the project management equivalent of time-travel debugging.
  • Custom workflow triggers. Future automation rules can trigger on specific event types - "when a card moves to the Done column, notify Slack" or "when a card is assigned, add it to the assignee's personal board." The event log provides the event stream for these triggers.

Event sourcing is not a new idea - it has been used in financial systems, distributed databases, and game engines for decades. What is new is applying it to project management, where the combination of collaborative editing, undo requirements, and accountability demands makes it a natural fit. The Flux activity log is the result of taking this pattern seriously.

For teams that want to explore kanban boards with built-in event sourcing, the kanban board template includes columns that are pre-configured for cycle time tracking via the activity log.

// Every action logged

Undo anything. Audit everything.

Event-sourced activity logs with conflict-aware undo. See who did what, when, and why. Free to start.