How Maka's Transcript Projection Derives UI Views from the Event Log

Maka derives UI views from its immutable event log by incrementally projecting stored messages through a pipeline that materializes canonical view models, overlays live and incremental updates, and reconciles object identities to preserve React rendering stability.

Apache Maka records every user interaction, tool execution, and system change as an append-only runtime event stream rather than mutable UI state. The transcript projection layer transforms this raw event log into presentation-ready structures without rebuilding the entire view on each render. This architecture ensures that React components receive stable references to unchanged data, enabling efficient differential updates based on object identity rather than deep equality checks.

The Event Log Foundation

At the core of the system lies an ordered list of StoredMessage objects representing each conversation turn. These messages contain raw markdown content, tool call metadata, and other execution details. Rather than passing these raw events directly to components, the UI consumes them through the TranscriptProjection interface defined in packages/ui/src/transcript-projection.ts.

The projection operates on TranscriptProjectionInput, which bundles the messages array with locale settings, optional live turn data, and incremental shell-run updates. This input remains immutable; the projection engine derives new view models while preserving references to unchanged objects.

The Five-Stage Projection Pipeline

The transformation from event log to UI-ready structures follows a strict sequence of pure functions that progressively enhance and stabilize the view model.

Stage 1: Materializing Stored Messages

The pipeline begins in packages/ui/src/materialize.ts, where the materializeTurns(messages, locale) function transforms raw StoredMessage objects into an array of TurnViewModel instances. Each view model encapsulates rendered markdown, a list of ToolActivityItem objects, and display flags such as compact and headingLevel.

This step produces the canonical transcript representation—the base state onto which all incremental updates are applied.

Stage 2: Overlaying Live Turns

When the system generates a response stream, the overlayLiveTurn(settledTurns, liveTurn) function injects a provisional turn into the settled array. This allows the UI to render in-flight content—such as streaming LLM output—without mutating the underlying event log. The overlay creates a temporary view model that merges with the canonical turns only for display purposes.

Stage 3: Applying Shell-Run Updates

As background tools execute, they emit ShellRunUpdate objects that report incremental progress. The projection handles these through two operations:

  1. foldShellRunUpdates(updates) collapses the update array into a map of ShellRunOverlayEntry objects indexed by turn ID.
  2. applyShellRunOverlay(turns) traverses the turn array, using applyShellRunOverlayEntry to patch the tools array of affected turns with the latest execution results.

This mechanism updates tool status indicators and output panels without regenerating the entire transcript.

Stage 4: Reconciling Identities for Stable Rendering

To prevent React from unnecessarily re-rendering unchanged turns, the reconcileTurnIdentities(previous, next) function compares structural values using valuesEqual. When two turns are semantically identical, the function retains the previous object reference, ensuring that TurnViewModel instances maintain referential equality across projection cycles. This step is critical for performance in long conversations where only the latest turns change.

React Hook Integration

The useTranscriptProjection hook in packages/ui/src/use-transcript-projection.ts exposes the projection engine to React components. It initializes a singleton projector via createTranscriptProjection() and memoizes the projection result:

// packages/ui/src/use-transcript-projection.ts
import { createTranscriptProjection } from './transcript-projection.js';
import { useMemo } from 'react';

export function useTranscriptProjection(input) {
  const projector = useMemo(() => createTranscriptProjection(), []);
  return useMemo(() => projector.project(input), [
    input.sessionId,
    input.locale,
    input.messages,
    input.liveTurn,
    input.shellRunUpdates,
  ]);
}

The dependency array ensures that projection only recalculates when the underlying event data changes, while identity reconciliation guarantees that unchanged turns return identical references.

Rendering the Derived Views

UI components such as ChatView in packages/ui/src/chat-view.tsx consume the stable TurnViewModel array directly:

// packages/ui/src/chat-view.tsx
import { useTranscriptProjection } from './use-transcript-projection.js';

function ChatView(props) {
  const turns = useTranscriptProjection({
    sessionId: props.sessionId,
    locale: props.locale,
    messages: props.messages,
    liveTurn: props.liveTurn,
    shellRunUpdates: props.shellRunUpdates,
  });

  return (
    <div className="maka-transcript">
      {turns.map(turn => (
        <Turn key={turn.turnId} viewModel={turn} />
      ))}
    </div>
  );
}

Because reconcileTurnIdentities preserves object identity for stable turns, React's diffing algorithm can efficiently update only the specific DOM nodes corresponding to new or modified content.

Summary

  • Immutable Event Log: Apache Maka stores all interactions as StoredMessage objects in an append-only stream.
  • Materialization: materializeTurns() in packages/ui/src/materialize.ts converts raw events into TurnViewModel instances.
  • Live Overlays: The system supports streaming content through overlayLiveTurn() without mutating canonical state.
  • Incremental Tool Updates: foldShellRunUpdates() and applyShellRunOverlay() merge background execution progress into the view.
  • Identity Preservation: reconcileTurnIdentities() ensures stable React rendering by reusing object references for unchanged turns.
  • Hook Abstraction: useTranscriptProjection() provides a memoized interface to the projection pipeline for UI components.

Frequently Asked Questions

How does Maka prevent unnecessary React re-renders when the event log grows?

Maka prevents unnecessary re-renders through identity reconciliation. The reconcileTurnIdentities() function compares the structural values of previous and newly projected turns using valuesEqual. When turns are semantically identical, the function returns the previous object reference, ensuring React's diffing algorithm recognizes them as unchanged. This allows long transcripts to update efficiently even as new messages arrive.

What is the purpose of the liveTurn overlay in the projection pipeline?

The liveTurn overlay enables real-time streaming of partial LLM responses without corrupting the canonical event log. The overlayLiveTurn() function injects a provisional turn into the settled turn array, allowing the UI to display in-flight content. Once the stream completes, the live turn is removed and replaced by the permanent StoredMessage in the event log, ensuring the projection remains deterministic.

How do shell-run updates integrate with the transcript view models?

Shell-run updates propagate through a two-phase folding process. First, foldShellRunUpdates() collapses incoming ShellRunUpdate arrays into a map of ShellRunOverlayEntry objects. Then, applyShellRunOverlay() patches the tools array within each affected TurnViewModel. This updates tool execution status and output panels incrementally without regenerating the entire transcript projection.

Where does the transcript projection hook initialize its state?

The useTranscriptProjection hook initializes a singleton projection instance via createTranscriptProjection() using React's useMemo() with an empty dependency array. This ensures the projection engine persists across component re-renders while the hook's second useMemo() call recalculates the view model only when messages, locale, liveTurn, or shellRunUpdates change.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →