# Understanding commitToolPrepared (T1) and commitToolOutcome (T2) in Apache Maka's Tool Execution

> Learn how Apache Maka uses commitToolPrepared T1 and commitToolOutcome T2 for robust tool execution. Discover how these phases ensure crash recovery and exactly-once semantics for your operations.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: internals
- Published: 2026-08-26

---

**Apache Maka uses a two-phase persistence mechanism where `commitToolPrepared` (T1) records immutable tool call details before execution, and `commitToolOutcome` (T2) persists the final result after completion, enabling crash recovery and exactly-once semantics.**

Apache Maka executes LLM-provided actions inside a durable **tool runtime (TR)** designed to survive crashes, restarts, and unexpected interruptions. To guarantee that no tool call is lost or executed twice, the runtime persists two distinct journal entries—`commitToolPrepared` (T1) and `commitToolOutcome` (T2)—in the SQLite runtime store, creating a durable audit trail from dispatch to completion.

## The Two-Phase Durability Model

Maka's tool execution follows a strict two-phase commit pattern that separates preparation from finalization. This separation ensures that the runtime can recover deterministically regardless of when a failure occurs.

| Journal Entry | Architecture Symbol | When Written | Contents |
|---------------|--------------------|--------------|----------|
| **Prepared** | **T1** (`commitToolPrepared`) | Before tool dispatch | Immutable replay data: operation ID, journal event ID, dispatch event, tool name, canonical argument hash, recovery mode, and timestamps |
| **Outcome** | **T2** (`commitToolOutcome`) | After tool returns | Final runtime event (success/failure), commitment timestamp, and linkage to the prepared entry via `journalEventId` |

Together, these entries form the backbone of Maka's exactly-once execution guarantee.

## Step T1: Persisting Tool Preparation with commitToolPrepared

Before dispatching a tool, the runtime constructs a **Prepared** journal entry using the `CommitToolPreparedInput` type. This entry captures all information necessary to replay the tool call later if the process crashes.

The runtime calls `SqliteRuntimeStore.commitToolPrepared()` (implemented in [`packages/storage/src/sqlite-runtime-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/sqlite-runtime-store.ts) at lines 1666–1684), which inserts a row into the `tool_prepared` table. This method returns a `ToolCommitResult` containing:

- **`created`**: Boolean indicating whether this is a new entry or a duplicate
- **`runtimeEventSeq`**: Sequential event number for the session ledger

This persistence step becomes the **source of truth** for recovery routines. If the system crashes after writing T1 but before obtaining the tool result, the runtime can locate the prepared record and re-invoke the tool with identical arguments.

## Step T2: Recording Final Results with commitToolOutcome

Once the tool finishes—or throws a `ToolOutcomeUnknownError` during boundary-crossing scenarios—the runtime builds an **Outcome** event using `CommitToolOutcomeInput`. This event reflects the final state: success, failure, or partial outcome.

The runtime invokes `SqliteRuntimeStore.commitToolOutcome()` (lines 1746–1765 in [`packages/storage/src/sqlite-runtime-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/sqlite-runtime-store.ts)), which writes to the `tool_outcome` table and updates the journal state to `outcome_committed`. This operation links the outcome to the previously prepared entry via the same `journalEventId`, completing the lifecycle.

The persisted outcome enables **exactly-once semantics** during recovery. On restart, the runtime checks for existing outcome records before re-executing tools, preventing duplicate operations.

## Implementation Details and Recovery Mechanics

The interaction between T1 and T2 provides several critical guarantees:

- **Duplicate Detection**: The `created` flag in `ToolCommitResult` allows the runtime to identify idempotent retries and avoid double-committing journal entries
- **Deterministic Recovery**: By re-reading the prepared entry from `tool_prepared`, the runtime can reconstruct the exact tool call context after a crash
- **Boundary-Crossing Safety**: When tools execute in remote sandboxes, failures are captured as `ToolOutcomeUnknownError`, prompting retry logic that relies on the immutable T1 preparation record

According to the architecture documentation in [`docs/architecture/runtime-resume-architecture.md`](https://github.com/apache/maka/blob/main/docs/architecture/runtime-resume-architecture.md), the flow appears as:

- **T1** – "TR → DB: commitToolPrepared" (runtime writes preparation record)
- **T2** – "TR → DB: commitToolOutcome" (runtime writes final result)

## Code Example: Committing Tool Lifecycle

The following TypeScript demonstrates how a tool runtime interacts with the storage layer to execute the two-phase commit:

```typescript
// Phase 1: Prepare the tool call (T1)
const prepared: CommitToolPreparedInput = {
  operationId: opId,
  journalEventId: jeId,
  runtimeEvent: dispatchEvent,
  dispatchRuntimeEvent: dispatchEvent,
  providerToolCallId: toolCallId,
  toolName: 'search',
  canonicalArgsHash: canonicalToolArgsHash(args),
  recoveryMode: 'auto',
  committedAt: Date.now(),
};

const prepResult: ToolCommitResult = await runtimeCommitSink.commitToolPrepared(prepared);
// prepResult.created === true indicates first-time recording

// Phase 2: After tool execution, commit the outcome (T2)
const outcome: CommitToolOutcomeInput = {
  operationId: opId,
  journalEventId: jeId,
  runtimeEvent: outcomeEvent,   // ToolCompleted or ToolFailed event
  committedAt: Date.now(),
};

const outResult: ToolCommitResult = await runtimeCommitSink.commitToolOutcome(outcome);
// outResult.created === true confirms outcome persistence

```

This pattern appears throughout the test suite in [`packages/storage/src/__tests__/sqlite-runtime-store.test.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/__tests__/sqlite-runtime-store.test.ts), which verifies durability and idempotency by repeatedly invoking `store.commitToolPrepared` and `store.commitToolOutcome` under simulated failure conditions.

## Summary

- **Two-phase persistence**: `commitToolPrepared` (T1) writes immutable dispatch details before execution, while `commitToolOutcome` (T2) records the final result after completion
- **Crash recovery**: The `tool_prepared` table enables deterministic replay of interrupted tool calls by storing canonical arguments and metadata
- **Exactly-once semantics**: The `tool_outcome` table prevents duplicate execution by tracking which operations have already completed
- **Idempotency support**: Both `commitToolPrepared` and `commitToolOutcome` return a `created` flag in `ToolCommitResult` to detect duplicate commit attempts

## Frequently Asked Questions

### What happens if Apache Maka crashes after T1 but before T2?

If the system crashes after `commitToolPrepared` (T1) succeeds but before `commitToolOutcome` (T2) completes, the recovery routine reads the pending entry from the `tool_prepared` table using the `journalEventId`. The runtime then re-invokes the tool with the exact same arguments (verified via the canonical argument hash) and proceeds to commit the outcome once execution finishes.

### How does Maka prevent duplicate tool execution during recovery?

Maka prevents duplicates by checking the `tool_outcome` table before re-executing any tool. If `commitToolOutcome` (T2) already recorded a result for the given `journalEventId`, the runtime skips execution and returns the persisted outcome. The `created` boolean in `ToolCommitResult` further helps callers identify whether they are processing a new request or handling a retry.

### What data is stored in the tool_prepared versus tool_outcome tables?

The `tool_prepared` table stores immutable dispatch context including the operation ID, journal event ID, tool name, canonical argument hash, recovery mode, and the dispatch runtime event. The `tool_outcome` table stores the final runtime event (success or failure status), the commitment timestamp, and maintains a foreign key relationship to the prepared entry via `journalEventId`.

### Where can I find the implementation of these commit methods?

Both methods are implemented in [`packages/storage/src/sqlite-runtime-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/sqlite-runtime-store.ts). The `commitToolPrepared` method resides around lines 1666–1684, while `commitToolOutcome` appears at lines 1746–1765. The public interfaces are defined in [`packages/runtime/src/runtime-commit-sink.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/runtime-commit-sink.ts), and the event type definitions live in [`packages/core/src/events.ts`](https://github.com/apache/maka/blob/main/packages/core/src/events.ts).