While you're reading Claude's response, a background speculation system is already pre-computing the next turn. It uses an overlay filesystem so speculative work can't damage real files. When you accept the suggestion, the pre-computed result is injected instantly — saving seconds per turn.
The system triggers when a prompt suggestion appears (after 2+ assistant turns). It forks a background agent to pre-compute what would happen if the user accepts that suggestion — running real API calls against the real Claude model.
tengu_chomp_inflection) generates a suggested next prompt. Suppressed during: permission prompts, plan mode, rate limits, elicitation.~/.claude/tmp/speculation/[PID]/[8-char-uuid]/. All file writes will be redirected here. A fresh AbortController (child of the main one) is created for instant cancellation.runForkedAgent() with the suggestion as prompt. This makes actual Claude API calls (not mocks) — costs real tokens. The agent inherits the current conversation context via cache-safe params for prompt cache hits.canUseTool interceptor that enforces speculation-safe constraints (see next section).checkReadOnlyConstraints() validates command is safe (ls, cat, git log). If not, speculation pauses at a bash boundary.CompletionBoundary:
complete (full response with token count),
bash (paused at unsafe command),
edit (paused at file write needing permission),
denied_tool (unsupported tool encountered).The key safety mechanism. All speculative writes go to an isolated temp directory. If speculation is aborted, the overlay is deleted — zero impact on real files.
When a write tool (Edit, Write, NotebookEdit) targets src/foo.ts:
writtenPaths setcwd/src/foo.ts to overlay/src/foo.tswrittenPaths setWhen a read tool targets a file that was previously written:
writtenPaths set for the relative pathThis means speculative edits are self-consistent — a Read after a speculative Write sees the speculated content, not the original.
~/.claude/tmp/speculation/[PID]/[8-char-uuid]/ — cleaned up with 3 retries, 100ms delay each. Directory created with mkdir -p at speculation start.abortSpeculation() fires immediatelytengu_speculation with outcome "aborted"When the first speculation completes fully (no boundary hit), the system immediately starts a second speculation for the NEXT turn — pre-computing two steps ahead.
Speculation's write behavior depends on the user's permission mode.
| Mode | Write tools during speculation | Effect |
|---|---|---|
acceptEdits | Allowed (via overlay) | Full speculation with file edits |
bypassPermissions | Allowed (via overlay) | Full speculation with file edits |
default | Blocked — hits boundary | Speculation pauses at first edit, user must approve |
plan | Blocked — hits boundary | Speculation pauses at first edit |
isSpeculationEnabled() checks process.env.USER_TYPE === 'ant'. External users don't have speculation yet. Toggleable via speculationEnabled in global config.| Field | Type | Purpose |
|---|---|---|
id | string (8 chars) | Unique speculation session ID |
abort | () => void | Kill switch — signals forked agent to stop |
messagesRef | Mutable ref | Accumulates generated messages in real-time |
writtenPathsRef | Set<string> | Relative paths written to overlay |
boundary | CompletionBoundary | null | What stopped speculation (complete, bash, edit, denied_tool) |
isPipelined | boolean | Second-order speculation flag |
pipelinedSuggestion | object | null | Next-turn suggestion for pipelined spec |
Every speculation logs a tengu_speculation event with outcome, duration, tools executed, boundary type, and message count.
| Metric | Description |
|---|---|
speculation_id | 8-char UUID |
outcome | accepted / aborted / error |
duration_ms | How long speculation ran |
tools_executed | Count of successful tool calls |
boundary_type | complete / bash / edit / denied_tool / null |
is_pipelined | Whether this was a second-order speculation |