How a Turn Works
A turn is what happens between your prompt and the moment there is nothing left for anyone to decide about it. Understanding its shape makes the rest of heph predictable, so this chapter lays it out once.
prompt ──► rounds ──► gates ──► review ──► apply ──► done
▲ │
└────── feedback ◄───┘ (a rejection or comment sends the
model another round in the same turn)
Rounds
A round is one request to the model and its reply. The reply is text, tool calls, or both. Heph runs each tool call in order, through the permission engine, feeds the results back, and the next round begins. Rounds continue until the model stops with nothing more to do.
Two things end the rounds early. You can interrupt (Interrupting). Or the model can hit its output limit; heph doubles the limit and re-sends once if a tool call was cut off mid-input, and otherwise ends the turn there with the text it has.
There is a ceiling of 200 rounds per turn, so a model that never says it is done cannot spend without end. Real turns are a handful of rounds.
What a tool returns stays in the model's context for the rest of the turn and is
sent again on every round after, so heph keeps reads small. view returns a
file whole only up to 400 lines; a longer file comes back as its outline,
the functions, types, impl blocks, classes, or headings it defines with the
lines each spans, and the model reads the range or the one item it needs from
there (outline is also a tool on its own). The outline is found from the text
by pattern, so it is approximate; it covers Rust, Python, JavaScript and
TypeScript, Go, Ruby, shell, the C-like languages, Markdown, Norg, TOML, YAML,
and Makefiles, and a long file of another kind comes back as its first lines.
Norg includes star headings and TODO list entries, even in a flat file like
TODO.norg; view(symbol) uses the title without its marker or status
extensions. Code/metadata, macro definitions, comments, and examples are
skipped. The scanner is approximate and does not interpret every Norg construct.
The system prompt tells the model to read this way.
The worktree
The model never works in your files. When a turn begins, heph fetches the conversation's upstream from your repository, the tip of the conversation's own branch once it has one and of its base branch until then (Branches), and replays this conversation's previously accepted private state on top of it, as a rebase would, then checks the result out in the conversation's worktree. What your checkout has checked out does not matter: switching branches there changes no conversation. Commits that appeared on the upstream since the last turn, whether you made them or another conversation published them, come in underneath the conversation's own change; they never show up as something for it to undo. Where a new commit and the conversation changed the same lines, the commit's text stands and the model is handed the displaced text to reapply, which then goes through review like any other edit. Dirty and untracked files in your checkout are not imported.
Every write the model makes is committed on a private shadow chain with its stated reason. Accepted state remains in this conversation across turns, which lets the agent build and test cumulatively without exposing unfinished changes to your checkout or to another conversation. Anything else the worktree holds when a turn begins, such as a file an interrupted turn wrote or one you edited there by hand, is carried onto the new base the same way and shown in that turn's review; nothing in the worktree is silently dropped or silently adopted.
Agent-run Git commands in the worktree use your source directory's configured
user.name and user.email, including global config and conditional includes.
Heph copies these into the shadow when it opens and refreshes them on reopening,
so commits and rebases need no identity setup. Heph's internal snapshots keep
their own heph <heph@localhost> identity. Hooks and signing settings are not
copied from your repository.
Paths your repository ignores are not copied, so target/ and node_modules/
are not duplicated; the Worktree chapter says how to
link them in when the model's commands need them. CARGO_TARGET_DIR points at
one directory per project shared by every worktree, so a build in the worktree
reuses your dependencies.
Markdown formatting
For named .md or .markdown files, the model can use markdown_format
instead of running a formatter command. Heph embeds yucky and formats at 80
columns without spawning a process or generating HTML. Formatting changes enter
normal review; check = true reports differences without writing. The result
lists file statuses and parser warnings, not whole documents. Directories and
symlinks are refused, and every named file is checked before any write.
The tool is Level 1 in both modes, so plan turns do not offer it. Design turns can format files within their configured design paths. See the tools reference for its parameters.
Gates
When the model stops, the acceptance gates run on what it changed:
commands you configure, such as cargo test, run in the worktree. A gate that
fails goes straight back to the model with its output; the model gets a fixed
number of tries per gate before heph shows you the review with the failure
attached. You never review something the gates could have caught.
Review
Then the turn's diff, everything the model changed since your snapshot, is presented for review, each hunk with the rationale the model gave for it. You accept, reject, or comment, hunk by hunk, in heph's own screen or in lazygit or another tool you configure.
What you accept is committed as the accepted state. What you reject is recorded as a constraint: the model is told, in your words, what was refused and why, and told not to propose it again in this turn.
Another round, or private acceptance
If you rejected or commented on anything, the model gets another round in the same turn, with your feedback. It edits again; the gates run again on what you kept plus what it changed; a second review shows only the new change. This continues until you accept everything or the model gives up.
If you accept everything, heph advances only the conversation's private snapshot. Your checkout is unchanged. The next turn starts from that snapshot plus any commits that have appeared upstream in the meantime.
When the turn ends with accepted changes and no pending review, heph prepares a
publication candidate, passes its final gates, and offers to commit its drafted
message in a dedicated dialog. y publishes immediately without an editor; e
opens the message in your editor first. Neither needs another gate pass if
upstream is unchanged; n or Esc leaves the work private. The publication
lock is held while you answer. See Committing.
/commit is the explicit publication transaction: it integrates the private
snapshot onto current committed HEAD, runs the due gates again on that exact
tree, runs your hooks and signing, creates the commit, and only then updates the
corresponding paths in your checkout. An overlap with a newer upstream commit
stays private and must be resolved before publication.
What ends a turn
A turn ends when:
-
the model said it was done, the gates passed, and there was nothing to review or you accepted it all (
turn done); -
you interrupted it (
turn interrupted); its work is still reviewable; -
the model hit its output limit twice, declined the request, or filled its context window (
turn ended: ...); -
the model re-proposed a rejected change twice in one turn, at which point heph applies what you did accept and stops asking.
A turn that ends with changes you have not reviewed, because you were not there to review them, or because a gate kept failing, leaves them in the worktree and marks the conversation as waiting. Picking Up Where You Left Off covers what happens next.
What a turn costs
Every round is one API call, recorded with its token counts and cost at the
model's frozen rates. The status bar shows day/session/thread totals; the
end-of-turn line shows the turn's input/output/cache token and USD breakdown.
Estimated usage is labeled; absent usage is unavailable rather than a zero bill.
Every request is sent so that the provider's prompt cache covers as much of it
as it can: the tool definitions, the system prompt, the whole conversation
before this turn, and every round of this turn before the current one are marked
for caching, so a round pays the input rate for its own new tokens and the
cache-read rate (a tenth of it; heph models shows the rates side by side) for
everything before them. The cache figure on the status bar is the share of
prompt tokens served that way; in a long turn it climbs past 90%.
What a round carries is mostly tool results, and a result read in an earlier
turn is rarely needed again. At the start of each turn heph replaces the results
of the turns before it with one-line stubs,
[view src/auth.rs: 412 lines, elided], so a long conversation's rounds stay
about the size of the current turn. The model reads again what it needs.
Context says what is kept and why the default keeps no
earlier turn whole.