Apache Maka Crash Recovery: How the Runtime Resume Protocol Survives Process Failures
Apache Maka guarantees that a turn (an atomic interaction with a model) can survive a process crash and be safely resumed through a durable SQLite event log and a deterministic four-phase recovery protocol.
This article explains how Apache Maka implements crash recovery based on the runtime architecture documented in apache/maka. The design centers on an append-only RuntimeEventStore, a RecoveryResolver decision engine, and layered resume phases that range from basic safety checks to full side-effect reconciliation.
Core Components of Apache Maka Crash Recovery
Apache Maka's crash recovery system relies on three interconnected components that ensure deterministic, fail-closed behavior after unexpected termination.
RuntimeEventStore: The Durable Source of Truth
The RuntimeEventStore is a SQLite-backed append-only log (runtime.sqlite) that records every RuntimeEvent produced by the Runtime Host. It serves as the single source of truth for all recovery decisions.
Key characteristics of the store:
- Atomic commits — events are written transactionally before any acknowledgment to the model
- Prefix projection — recovery reads only the committed prefix of events, ignoring uncommitted writes
- Immutable history — once committed, events are never modified or deleted
The store implementation lives in packages/runtime/src/runtime-event-store.ts. During normal operation, it receives events from the Runtime Host through the SessionManager → AgentRun pipeline. After a crash, a new store instance opens the same SQLite file to begin recovery.
Crash Contract (Phase 0): The Safety Gate
The Phase 0 Crash Contract defines what can be safely replayed after a crash. This contract, documented in docs/architecture/runtime-resume-phase0-crash-contract.md, inspects the committed event prefix and produces a deterministic ResumePlan.
The contract applies a simple but critical rule:
- Safe replay — permitted if the committed prefix ends before any tool operation (
before_function_call,after_function_call, etc.) - Blocked — required if any tool operation appears in the prefix, since side effects may have occurred
This fail-closed design ensures Maka never guesses about unknown states. If the contract cannot verify safety, it blocks automatic resumption.
RecoveryResolver: The Authoritative Decision Engine
The RecoveryResolver implements the complete decision logic across all recovery phases. Located in packages/runtime/src/recovery-resolver.ts, it:
- Reads the committed prefix from RuntimeEventStore
- Applies the Phase 0-3 contract rules
- Emits deterministic decisions:
completed,indeterminate,corruption,safe_replay, orblocked
The RecoveryResolver never guesses. Unknown states are treated as failures, forcing manual intervention rather than risking incorrect replay. The architectural decision record in docs/architecture/runtime-recovery-resolver-adr.zh-CN.md documents this fail-closed philosophy.
The Four-Phase Runtime Resume Architecture
Maka structures crash recovery across four phases, with each phase adding capabilities while maintaining safety guarantees.
| Phase | Purpose | Safety Guarantee |
|---|---|---|
| Phase 0 — Crash Contract | Determine if prefix is replay-safe | No tool side-effects replayed; deterministic safe/blocked decision |
| Phase 1 — Safe Boundary | Enable automatic resumption for Desktop/CLI | Safe replay without manual intervention when contract permits |
| Phase 2 — Tool Boundary (T1/T2) | Capture tool execution state atomically | Tool dispatch events mark boundaries; journal is projection of event store |
| Phase 3 — Full Recovery | Side-effect reconciliation and continuation runs | Complete fail-closed recovery with idempotent re-execution |
Only Phases 0 and 1 are required for basic crash-only safety. Phases 2 and 3 add sophistication for complex scenarios involving tool side effects and multi-turn continuations.
The system architecture flows from Runtime Host → SessionManager → AgentRun → RuntimeEventStore, with recovery traversing this path in reverse through the RecoveryResolver.
Enabling Automatic Safe-Boundary Resume
Apache Maka supports optional automatic crash recovery through an environment flag. When enabled, the Runtime Host queries the RecoveryResolver and automatically continues safe turns.
# Enable safe-boundary resume for Desktop or CLI
export MAKA_RUNTIME_SAFE_BOUNDARY_RESUME=1
With this flag active:
- The Runtime Host opens the workspace's RuntimeEventStore
- Invokes RecoveryResolver.computeResumePlan()
- If the plan is
safe_replay, automatically re-executes the model turn - If
blocked, presents a manual "Resume" option in the UI or CLI diagnostic
Without the flag, all blocked turns require explicit user action.
Crash Recovery in Practice: A Step-by-Step Walkthrough
When a process terminates unexpectedly, Apache Maka follows this deterministic sequence:
-
Crash occurs — process receives
SIGKILLor equivalent; SQLite file contains committed event prefix -
Workspace reopened — new RuntimeEventStore instance opens
runtime.sqlitefrom the workspace -
Prefix projection — store identifies the last fully committed sequence of events
-
ResumePlan generation — RecoveryResolver applies Phase 0 contract rules:
- Ends before
before_function_call? →safe_replay - Contains any tool boundary event? →
blocked
- Ends before
-
Safe replay execution — for
safe_replay, Runtime Host re-executes from last model turn using recorded messages; no tool code runs -
Blocked handling — for
blocked, UI shows Resume button or CLI emits diagnostic; user chooses retry, edit, or abort
Throughout this process, the durable ledger remains read-only during decision-making. No mutations occur until a definitive safe replay is confirmed.
Code Example: Querying Recovery State
The following TypeScript demonstrates how applications can integrate crash recovery detection:
// Enable safe-boundary resume (optional)
process.env.MAKA_RUNTIME_SAFE_BOUNDARY_RESUME = '1';
import { RuntimeEventStore, RecoveryResolver } from '@maka/runtime';
async function evaluateCrashRecovery(workspacePath: string): Promise<string> {
const store = await RuntimeEventStore.open(workspacePath);
const resolver = new RecoveryResolver(store);
// Returns 'safe_replay' | 'blocked' | 'completed' | 'indeterminate' | 'corruption'
const plan = await resolver.computeResumePlan();
return plan;
}
// CLI integration example
(async () => {
const workspace = process.argv[2] || '/path/to/workspace';
const plan = await evaluateCrashRecovery(workspace);
switch (plan) {
case 'safe_replay':
console.log('✅ Crash recovery: safe to resume automatically');
// RuntimeHost.resumeTurn(workspace) replays model turn
break;
case 'blocked':
console.warn('⚠️ Crash recovery: manual intervention required');
process.exitCode = 1;
break;
case 'completed':
console.log('Turn already completed, no recovery needed');
break;
default:
console.error(`❌ Recovery failed: ${plan}`);
process.exitCode = 2;
}
})();
The RecoveryResolver class encapsulates all phase transitions, ensuring consistent behavior across Desktop, CLI, and embedded deployments.
Source Files for Apache Maka Crash Recovery
| Path | Description |
|---|---|
docs/architecture/runtime-resume-phase0-crash-contract.md |
Formal contract defining safe-replay vs. blocked outcomes |
docs/architecture/runtime-recovery-resolver-adr.zh-CN.md |
Architectural decision record for the RecoveryResolver |
packages/runtime/src/runtime-event-store.ts |
SQLite-backed durable event log implementation |
packages/runtime/src/recovery-resolver.ts |
Recovery decision engine with Phase 0-3 logic |
README.md (Local Data and Recovery section) |
Feature overview and environment flag documentation |
ARCHITECTURE.md |
System-wide architecture with Runtime Resume component placement |
Summary
- Apache Maka crash recovery depends on an append-only SQLite RuntimeEventStore as the single source of truth
- The Phase 0 Crash Contract deterministically classifies crashes as
safe_replayorblockedbased on whether tool operations appear in the committed prefix - The RecoveryResolver implements fail-closed decision logic across four phases, never guessing about unknown states
- Safe-boundary resume can be enabled via
MAKA_RUNTIME_SAFE_BOUNDARY_RESUME=1for automatic recovery in Desktop and CLI environments - All recovery operations are read-only until safety is confirmed, preserving ledger integrity
Frequently Asked Questions
What happens if Apache Maka crashes during a tool execution?
If the committed event prefix contains any tool boundary event (before_function_call, after_function_call, or after_function_response), the Phase 0 Crash Contract returns blocked. The turn cannot be automatically resumed because side effects may have occurred. The user must manually decide whether to retry, edit, or abort the turn.
How does Apache Maka ensure the event log isn't corrupted during a crash?
The RuntimeEventStore uses SQLite's atomic transaction guarantees. Events are committed to disk before any acknowledgment returns to the Runtime Host. During recovery, only the committed prefix is read—partial writes in the WAL or journal files are excluded by the prefix projection logic in runtime-event-store.ts.
Can I disable automatic crash recovery in Apache Maka?
Yes. Automatic safe-boundary resume is opt-in via the MAKA_RUNTIME_SAFE_BOUNDARY_RESUME environment variable. Without this flag, all turns that would qualify for safe_replay still require explicit user confirmation through the UI or CLI, matching the behavior of blocked turns.
What is the difference between Phase 1 and Phase 3 recovery?
Phase 1 (Safe Boundary) enables automatic resumption when the Phase 0 contract returns safe_replay—essentially crashes that occurred before any tool execution. Phase 3 (Full Recovery) handles complex scenarios with tool side effects through the complete RecoveryResolver decision table, including tool_recovery_decided events and continuation-run manifests for multi-turn recovery.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →