interaction: review)A review-&-merge gate is a human node that walks the operator through testing a change via a continuous companion↔human dialogue, then squash-merges the run’s worktree during the pause once the operator is satisfied. It is the moment a run stops being autonomous and asks a human to verify the work before it lands — the iterion equivalent of a GitHub “squash and merge” review, with an agent guiding the test.
It is a fourth interaction: mode on human nodes, alongside human,
llm, and llm_or_human.
human ship_review:
interaction: review
model: "anthropic/claude-sonnet-4-6" # the companion (writes test steps + verdict)
system: companion_system # the companion's contract prompt
output: review_verdict # decision / confidence / blockers — routes downstream
review_url: "" # optional env to open & test (studio Browser pane)
posture: human_required # human_required (default) | agent_verdict_ok
merge_strategy: squash # squash (default) | merge
merge_into: current # current (default) | none | <branch>
max_turns: 8 # dialogue asymptote backstop
workflow ship:
entry: implement
worktree: auto # REQUIRED — the gate merges this worktree
implement -> ship_review
ship_review -> done when "decision == 'approved'"
ship_review -> implement when "decision == 'changes_requested'" as fix_loop(5)
ship_review -> fail # default fallback
Reference workflow: examples/review-merge-gate.bot.
changes_requested edge (e.g. back to
the implementer).final_commit / final_branch / merged_into / merge_status=merged
on run.json, then advances to the terminal node. The run-end finalize is
idempotent (it skips when the gate already finalized).The dialogue is bounded by max_turns and trends to a verdict, so the gate
converges to an asymptote rather than looping (the rule every iterion
review loop follows).
posture: human_required (default) — the gate always waits for the
operator’s explicit action, even when the companion is satisfied. The
companion’s verdict is shown but never auto-merges.posture: agent_verdict_ok — a high-confidence companion approved
verdict auto-merges without a human click. This is “human-in-the-loop
off”: the agent’s judgement is trusted to ship.| field | values | meaning |
|---|---|---|
merge_strategy |
squash (default) | merge |
squash collapses the run’s commits into one; merge fast-forwards |
merge_into |
current (default) | none | <branch> |
target branch; none = create the storage branch only (branch-only review) |
The studio merge form can override the strategy and the squash commit
message per-merge. These reuse the same primitives as the post-run
deferred-merge (runtime.PerformDeferredMerge), so the guards are identical:
the target must be the currently-checked-out branch and the working tree must
be clean, else the merge is rejected (the run becomes resumable so the
operator can fix the tree and re-approve).
The review_url is a template (e.g. ``) resolved at
pause time. Pair the gate with an upstream agent that deploys/serves the
change and reports a URL — the gate links it into the studio Browser pane so
the operator can actually exercise the app. The provisioning step is entirely
custom-prompt-driven and optional; when review_url resolves empty the
link is hidden. iterion does not start or expose ports for you — the agent
reports whatever URL the operator should open (a cluster endpoint, a host
dev-server, a desktop build path).
interaction: review without worktree: auto. A review
gate squash-merges the run’s worktree; without one there is nothing to merge.review_url references an output of a node that does
not exist. The URL simply renders empty at runtime.A review gate also requires a companion model and an output schema (the
verdict), the same requirement as llm / llm_or_human modes.
The dialogue lives on a single Interaction (stable id, no loop suffix) as
an ordered turns array; each round appends a turn and re-pauses, so the
whole thread re-renders verbatim on resume. Events: review_turn,
review_verdict, review_merged (see
docs/persisted-formats.md). The merge outcome is the
standard final_* / merge_status fields on run.json — the same ones the
studio RunHeader and CommitsPanel already surface.
The operator’s action rides the resume answers map under reserved keys, so
no new endpoint is needed — POST /api/runs/{id}/resume and
iterion resume --answer both work:
| key | values |
|---|---|
__review_action |
reply | approve_merge | force_merge | request_changes |
__review_reply |
free text (for reply) |
__review_message |
optional squash commit message override |
__review_merge_strategy |
optional squash | merge override |
Example (force-merge from the CLI):
iterion resume --run-id <id> --file ship.bot \
--answer __review_action=force_merge