T1 and T2 Side Effect Boundaries in Apache Maka: A Complete Guide
T1 (Tool Invocation) persists the tool call intent before execution begins, while T2 commits the immutable result after the tool finishes, ensuring exactly-once semantics for side effects in Apache Maka's runtime.
Apache Maka is an open-source framework for building deterministic AI agents. Its execution runtime isolates tool side effects behind two durable commit points known as the T1 and T2 side effect boundaries. These boundaries guarantee that tool invocations and their outcomes are recorded exactly once, even in the event of process crashes or network failures.
What Are the T1 and T2 Side Effect Boundaries?
Maka’s runtime divides a tool’s lifecycle into two distinct transaction phases that bracket the actual implementation execution. This design prevents partial failures where a tool runs but its result is lost, or where a result is reported without the tool having actually executed.
| Boundary | Alternative Name | Timing | Durability Guarantee |
|---|---|---|---|
| T1 | TI (Tool Invocation) | After the runtime writes the tool_call journal entry, before the implementation function is invoked. |
The operation ID and call arguments are persisted; the invocation is recoverable even if the host crashes immediately after. |
| T2 | Outcome boundary | After the implementation returns, before the function_response is fed back to the model. |
The result is atomically committed; if T2 fails, the result is discarded and never reaches the model. |
Together, these boundaries create an exactly-once side effect window: the runtime can always replay a T1-recorded call, and it never exposes a partially committed T2 result.
How the TI (T1) Pre-Implementation Boundary Works
The TI boundary (also referred to as T1) marks the moment the runtime transitions from planning to execution. At this point, Maka allocates a unique operation ID and appends a tool_start or tool_call event to the SQLite journal.
Journal Persistence Before Execution
According to the source code, the runtime explicitly handles this phase as the first commit boundary. In packages/runtime/src/tool-runtime.ts, the optional Phase 2 commit boundary is initialized before the tool implementation runs:
https://github.com/apache/maka/blob/main/packages/runtime/src/tool-runtime.ts#L457
This line contains the comment "Optional Phase 2 T1/T2 commit boundary", indicating where the runtime prepares to record the invocation. Additionally, the ai-sdk-backend.ts file implements the SQLite-specific T1/T2 boundary logic that physically writes the journal entry to disk before yielding control to the tool’s code.
Code Example: Recording a Tool Call
The following pattern demonstrates how the T1 boundary is crossed when scheduling a tool:
import { ToolRuntime } from '@maka/runtime';
const runtime = new ToolRuntime();
// T1 boundary crossed here: journal entry persisted
await runtime.recordToolCall({
toolName: 'search',
args: { query: 'apache maka side effects' },
operationId: 'op-12345'
});
// Only after T1 succeeds does the implementation execute
const result = await executeSearchTool('apache maka side effects');
If the process crashes after recordToolCall resolves but before executeSearchTool begins, the runtime can resume and replay the operation using the persisted operationId.
How the T2 Outcome Boundary Works
Once the tool implementation completes, the runtime must decide whether to make the result visible to the agent. The T2 boundary is the atomic commit point where the function_response is written to the journal. Until T2 succeeds, the result is considered tentative and can be discarded.
Committing the Function Response
The T2 commit occurs in packages/runtime/src/tool-runtime.ts near the implementation of the outcome transaction. If the commit succeeds, the side effect is permanent. If it fails, the runtime raises a specific error to prevent the partial result from propagating.
Error Handling with RuntimeCommitBoundaryError
When T2 fails—due to disk I/O errors, database corruption, or power loss—the runtime throws a RuntimeCommitBoundaryError identifying the T2 stage:
https://github.com/apache/maka/blob/main/packages/runtime/src/tool-runtime.ts#L1835
This error construction RuntimeCommitBoundaryError('T2', error) forces callers to handle the failure explicitly, ensuring that the agent never consumes a result that was not durably stored.
Code Example: Handling T2 Commit Failures
Below is the canonical pattern for finalizing a tool execution and handling T2 failures:
try {
// Tool implementation already executed
const rawResult = await executeSearchTool(query);
// T2 boundary crossed here: result committed or exception thrown
await runtime.commitToolOutcome({
toolCallId: 'op-12345',
result: rawResult
});
// Result is now safe to return to the model
return rawResult;
} catch (error) {
if (error instanceof RuntimeCommitBoundaryError && error.boundary === 'T2') {
// T2 failed: result was discarded, must retry or escalate
console.error('T2 commit failed, outcome not persisted:', error.originalError);
throw new RetryableToolError();
}
throw error;
}
Architecture Documentation and Crash Contracts
The high-level semantics of these boundaries are documented in the architecture specifications. The runtime-resume-architecture.md file describes how Phase 2 is bounded by T1 and T2 to isolate the side-effect window:
https://github.com/apache/maka/blob/main/docs/architecture/runtime-resume-architecture.md#L156-L166
For crash recovery scenarios, the runtime-resume-phase0-crash-contract.md defines the contract for state consistency. Line 48 of this document includes a table specifying the semantics when the side effect is finished but the "outcome transaction (T2) not committed":
This contract ensures that host implementations can deterministically resume from a crash by checking which boundaries were crossed for any pending operation.
Summary
- T1 (TI) is the pre-implementation boundary that persists the tool call intent and operation ID before any side effects occur, enabling crash recovery.
- T2 is the post-execution boundary that atomically commits the
function_response; failures here trigger aRuntimeCommitBoundaryErrorand prevent partial results from reaching the model. - The implementation spans
packages/runtime/src/tool-runtime.tsfor core logic andpackages/runtime/src/ai-sdk-backend.tsfor the SQLite-specific persistence layer. - Architecture documentation in
docs/architecture/runtime-resume-architecture.mdformalizes the two-phase commit semantics, whileruntime-resume-phase0-crash-contract.mddefines recovery guarantees.
Frequently Asked Questions
What happens if T2 fails after the tool already executed?
If the T2 commit fails—due to a disk write error or process crash—the runtime throws a RuntimeCommitBoundaryError and discards the implementation result. The model never sees the partial outcome, and the host can retry the operation safely because the T1 boundary already guarantees the invocation intent was recorded.
Is TI the same as T1?
Yes. In the Maka codebase, TI stands for Tool Invocation, which is the specific phase that implements the T1 side effect boundary. Both terms refer to the moment the runtime persists the tool_call journal entry before invoking the actual tool implementation.
How does Maka recover from a crash between T1 and T2?
During recovery, the runtime scans the SQLite journal for operations that have a T1 record but lack a corresponding T2 commit. For these pending operations, Maka can either replay the invocation (if the implementation is idempotent) or return a specific resume error, depending on the crash contract defined in runtime-resume-phase0-crash-contract.md.
Where is the boundary logic implemented in the source code?
The core boundary logic resides in packages/runtime/src/tool-runtime.ts, specifically around line 457 for the T1/T2 initialization and line 1835 for the T2 error handling. The actual SQLite persistence that enforces these boundaries is implemented in packages/runtime/src/ai-sdk-backend.ts.
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 →