CONCEPTS · WORKFLOW ENGINES

Workflow engines

By default every Synthex command is prose: Markdown instructions the agent reads and follows. On Claude Code you can opt two pieces of that into a Workflow script instead — the reviewer fan-out in review-code/synthex:review-code, and the "are we done?" verdict behind every --loop. Prose stays the default, and nothing changes until you turn an engine on.

When an engine runs

An engine runs only when all of these hold, checked fresh on every invocation:

  1. You are on Claude Code.
  2. A Workflow tool is in the session's tool list.
  3. The engine's config key is set to workflow (it ships as prose).

The loop engine also stands aside when you pass --loop-isolated.

If any condition fails, the command runs its normal prose path. Nothing errors and nothing stalls. When code_review.engine: workflow is set but no Workflow tool is available,

review-code/synthex:review-code prints one line saying it is running the prose path instead. Every other harness (Codex, Gemini CLI, OpenCode, Grok) always runs prose.

# .synthex/config.yaml
code_review:
  engine: workflow # prose | workflow
  refute_pass: off # on | off; only read by the review engine

native_looping:
  engine: workflow # prose | workflow

The command's own instruction to call Workflow is the opt-in; you are not asked to confirm it each session. Headless runs (claude -p, CI) have no one to answer a permission prompt, so add an allow rule for the engine ahead of time — Workflow(synthex:review-code-engine), Workflow(synthex:loop-engine) — or run in auto or bypass permission mode.

The review engine

With code_review.engine: workflow, each review cycle in review-code/synthex:review-code hands its reviewer work to the synthex:review-code-engine script. One call runs one cycle:

  1. Parallel reviewers. Every configured reviewer runs at once, including prompt-file reviewers. Each must return a fixed envelope — findings, positives ("What's Done Well"), and a summary — instead of free-form Markdown, so nothing is lost to parsing.
  2. Multi-model, if enabled. When multi-model review applies, the orchestrator runs alongside the native reviewers and its findings join the same list. If its output can't be read, that cycle continues with the native findings.
  3. Dedupe in code. Findings are merged without a model call: by matching id, by a very similar title in the same file, or by a loosely similar title at the same location (same symbol or overlapping lines). Two reviewers who describe one bug in different words end up as one finding that keeps both reviewers' categories.
  4. Optional refute pass. See below.
  5. Verdict and summary. The verdict is computed from severities, not asked of a model: FAIL on any critical or high finding, WARN on any medium, PASS otherwise. One agent call writes the two-to-three-sentence summary.

What changes for you

  • Same report. The rendered Code Review Report uses the same headings, reviewer table, and severity sections as the prose path. Reviewer wording differs, because the engine re-renders structured output rather than passing each reviewer's prose through.
  • Better attribution. The engine records which reviewer raised each finding itself, so you don't get findings labeled with an unknown source.
  • Fewer duplicates. Cross-reviewer duplicates collapse more reliably than they do when a model consolidates them.
  • Same loop. The fix-and-re-review loop still belongs to the command. On a FAIL it fixes, then calls the engine again for the next cycle with a summary of what was still open. Your review_loops settings apply exactly as before.
  • Failed reviewers degrade. A reviewer that errors shows as FAIL (no response) in the reviewer table; the review still completes.

The refute pass

code_review.refute_pass: on adds an adversarial check, and only takes effect when the review engine is running. Every critical or high finding that survives dedupe goes to three independent refuters, each looking at that one finding through a different lens: correctness, whether it reproduces, and security impact. A finding survives unless two or more refuters reject it.

A refuted finding is dropped from the rendered report and no longer counts toward the verdict, so it can't force a FAIL it doesn't justify. The engine still returns it, with the refuters' reasoning, for anyone inspecting the raw results.

The cost is three extra Sonnet calls (at low effort) per critical or high finding, per cycle. A cycle with no critical or high findings costs nothing extra.

This is separate from code_review.verification, which adds a lighter check inside the reviewers themselves on the prose path. See Configuration.

The loop engine

With native_looping.engine: workflow, every --loop command — next-priority/synthex:next-priority,

loop/synthex:loop, review-code/synthex:review-code, refine-requirements/synthex:refine-requirements, and write-implementation-plan/synthex:write-implementation-plan — asks the synthex:loop-engine script for its verdict at each iteration, instead of judging completion itself.

Each iteration:

  1. The command does the iteration's work as usual, then summarizes what changed and what remains.
  2. One read-only Sonnet agent reads that summary and the artifact (the plan, the PRD, the review report) and decides: done, keep working, idle, or blocked on a human question. For next-priority/synthex:next-priority it also picks the next batch of tasks from the plan.
  3. If the verdict is done, a second, independent read-only agent checks every completion condition on its own and lists anything unfinished. The loop finishes only if that list is empty.
  4. The command acts on the result: finish, work the selected tasks, ask you the blocking question, or wait for files to change.

The engine agents can only read. Worktrees, Tech Leads, plan edits, questions to you, and loop state all stay with the command, so a verdict that arrives late or gets superseded has changed nothing.

What changes for you

  • A second opinion on "done". A loop no longer ends because the agent doing the work decided it had finished. If the confirming agent disagrees, or the verdict can't be read, the loop switches to the prose path for the rest of the run rather than stopping early.
  • A pause between iterations. The session ends its turn with Waiting for loop verdict <run-id>. while the engine runs, then picks up when the result arrives. Cancel, resume, and compaction recovery work as they do for prose loops.
  • A little extra cost. One Sonnet call per iteration, plus one more when the verdict is done.

If the engine can't be launched, the command prints Loop engine unavailable; continuing on the prose path. and carries on.

When to opt in

Review engine. Turn it on if you run review-code/synthex:review-code often with three or more reviewers, or alongside multi-model review, and duplicate or unattributed findings are slowing you down. Add refute_pass: on if false-positive critical and high findings are blocking merges and the extra calls are worth it to you.

Loop engine. Turn it on for long unattended loops — a next-priority/synthex:next-priority run through a whole plan, or a review loop you leave running — where stopping early on a premature "done" costs more than one extra call per iteration.

Stay on prose if you use more than one harness on the same project and want identical behavior everywhere, or if your sessions don't have a Workflow tool.

Next