Skip to content

Review & Merge — Conceptual Deep Dive ​

Purpose & Mental Model ​

Agentweaver's review and merge subsystem answers one product-defining question: how can agent-produced work move quickly while preserving human oversight at every irreversible step?

The design separates three concerns that are easy to accidentally blur:

  1. Workflow declarations identify the review gates and their execution edges.
  2. Review execution pauses a live run and waits for a decision.
  3. Merge execution applies an already-reviewed tree to the target branch under repository and database guards.

That separation is the reason the system can support standalone runs, coordinator child runs, automated reviewers, human approval, request-changes loops, and collective assembly without every path inventing its own safety model.

A useful rebuilding rule is: review approves intent to proceed; merge proves the repository can actually accept the result. Approval and merge are related, but they are not the same operation.

Core Design Invariants ​

These invariants define the subsystem:

  • No hidden merge path. Generated changes reach the target branch only through an explicit merge executor or coordinator assembly merge.
  • Gates are durable pause points. A human review request is represented in run state and stream events, not only in an in-memory callback.
  • Review decisions have arbitration guards. Pending review requests are consumed atomically and status transitions use compare-and-swap. Matching terminal replays can return the existing result; competing active decisions can return conflict. Live and deferred delivery have different ordering, described below.
  • Request changes loops back to work. A reviewer can send feedback to the producer instead of choosing between blind approval and terminal rejection.
  • Automated review is policy, not authority by itself. RAI and rubberduck gates can pass, request revision, or fail/route the graph, but the human-review gate is the explicit human-oversight point for irreversible actions in the default runtime path.
  • Merge is repository-serialized. Even after approval, repository-level merge locks and run-status CAS guards prevent two merges from racing the same base checkout. PostgreSQL deployments use session advisory locks so this guard spans API replicas; SQLite/local development uses a process-wide semaphore.
  • Coordinator children do not merge. Child runs produce assemble-ready branches. The parent coordinator assembles, reviews, merges, and records the integrated outcome.
  • Fail closed on unbound workflow nodes. An unsupported executable node must fail binding rather than silently disappear.

Workflow-declared review gates ​

Review gates belong to the selected workflow definition. RunWorkflowFactory.ResolveEffectiveWorkflowAsync resolves the workflow and returns it unchanged; it does not load named project review-policy files or inject a policy overlay. Blueprint validation accepts only review_policy: default (apps/Agentweaver.Api/Runs/RunWorkflowFactory.cs:1495–1517; apps/Agentweaver.Api/Blueprints/BlueprintService.cs:111–115).

Relevant gate kinds include:

  • RAI — Responsible AI review. It can pass, request revision, or fail safe on content-safety.
  • Rubberduck — automated peer/sanity review. It can pass or request changes.
  • Build & Test — automated verification of an applicable assembled code artifact.
  • Human review — explicit human approval. It can approve, request changes, or decline.

The runtime binds declared nodes and edges to concrete executors. Compatibility adapters and historical comments mentioning policy-prefixed gates do not establish a configurable registry or composer. See Binding declarative nodes to runtime execution.

The lifecycle below describes a review-bearing standalone workflow, not a guarantee that every custom workflow declares the same gates. Coordinator children use a separate trimmed graph; their parent resolves applicable aggregate gates from its selected workflow.

Human and Automated Reviewers ​

Agentweaver treats automated and human reviewers as different kinds of gates with the same graph vocabulary.

Automated gates are executable reviewers:

  • RAI runs through a Responsible AI reviewer agent and emits a verdict.
  • Rubberduck runs through an AI critique reviewer and maps PASS to forward progress and REVISE to request changes.

Human gates are request ports:

  • The workflow emits a review request containing the run id, tree hash, diff, step count, and RAI context.
  • The watch loop persists the run as awaiting review and stores a pending request.
  • The client submits approve, request-changes, or decline.
  • The pending request is consumed once and the live workflow resumes on the selected edge.

The key distinction is accountability. Automated reviewers can help decide whether work is ready for a person, but human review is the point where a named user approves or rejects the irreversible action. The run records the reviewer on merge-related status transitions when that reviewer is known.

Single-Run Review Lifecycle ​

A standalone workflow with RAI, human review, and merge has the conceptual shape:

  1. The agent produces a tree hash and diff in an isolated worktree.
  2. RAI reviews the output.
  3. A human-review request is emitted.
  4. The run waits in awaiting_review.
  5. The reviewer chooses approve, request changes, or decline.
  6. Approval enters merge; request-changes returns to the agent; decline terminates.

The important part is the pause. awaiting_review is not a UI-only label. It is the durable point where the workflow can stop streaming, the browser can disconnect, and a later caller can still see that the run needs a decision.

Approve vs Request Changes ​

Approve and request-changes both start from the same review gate, but they intentionally diverge.

Approval does not edit files. It authorizes the existing reviewed tree to proceed toward merge. Request-changes does edit the future path: it carries reviewer feedback back into the producer's next turn and increments the revision loop.

For POST /api/runs/{id}/review, the ordering is explicit (apps/Agentweaver.Api/Endpoints/RunEndpoints.cs:845–1053):

PathArbitration and effect
Local live approvalConsume the pending request, then send the decision to the workflow. Approval does not CAS the run to merging in the HTTP handler; the merge executor later acquires the repository lock before its merge CAS.
Local live request-changes / declineCAS awaiting_review to in_progress / declined before removing the pending request, then resume the workflow.
No local workflow, durable pending request existsPersist the deferred decision first; request-changes/decline then attempt their status CAS. The owner workflow consumes the deferred response. An approval response labelled merging is not proof that the merge CAS or Git merge has occurred.
Neither live workflow nor pending requestUse direct approval/decline fallback. Direct request-changes returns 409 and restores awaiting_review; it cannot reconstruct a revision workflow.
Replay / competing callerMissing pending requests or losing status transitions can return 409; a matching already-merged approval or already-declined non-approval returns the existing terminal result.

There are two request-changes surfaces:

  • The review decision path can send request_changes through the live workflow so the graph loops back.
  • The dedicated request-changes endpoint validates and sanitizes a comment, records a revision audit row, abandons stale checkpoints, clears run-scoped shell approvals, and starts a fresh revision workflow on the same worktree.

Both preserve the same invariant: after a reviewer rejects the current output, the existing tree is not merged; work returns to a producer path with explicit feedback.

Reviewer Rejection and Lockout ​

The implemented lockout model has two layers.

First, workflow production and review are distinct responsibilities. In a review-bearing workflow, the agent executor proposes a tree; the review decision resumes the graph toward revision, decline, or guarded merge. This is not a claim that every custom workflow enforces two-person approval.

Second, pending consumption and status transitions arbitrate active decisions. A consumed live gate cannot accept another response. Losing CAS operations return conflict, while matching already-terminal replays can return the recorded result. These guards are not a separate agent-author rotation rule.

The approval and request-changes sequence shows the shared pending-request and CAS arbitration; a second lockout diagram would duplicate it.

The standalone endpoint requires run access at ProjectRole.Contributor; an additional pending-request owner check applies to projectless legacy runs (RunEndpoints.cs:875, :936–938, :1008–1011). The coordinator assembly gate has its own owner-scoped delivery contract. Neither is a general two-person rule preventing a human requester from approving their own run. Coordinator agent-author rotation is a different mechanism: a resumable rejected target may keep its author; a fresh-dispatch decision attempts scoped rotation, with a bounded same-author fallback when no alternate is eligible. See resilient reviewer rejection.

Coordinator Collective Review ​

Coordinator orchestration changes the unit of review. Child runs do not individually ask for human approval and do not merge. They stop at assemble-ready, carrying branch, tree hash, diff, and safety context back to the parent.

The parent coordinator then performs one collective assembly pipeline:

  1. Ensure all subtasks are assembly-eligible.
  2. Build an integration branch from child branches in dependency order.
  3. Resolve workflow-declared aggregate gates and Build & Test applicability; run those gates in their resolved order.
  4. At the authored human gate, persist one review request over the combined output. Collective RAI RED also parks at durable human review; RAI REVISE enters explicit steering, with durable human escalation when the decider chooses Proceed.
  5. On approval, continue any remaining authored gates, then merge the integration branch into the originating branch and run Scribe.
  6. On request-changes, scope structured target files and dependent rebuilds, then let the coordinator explicitly choose in-place revision, fresh dispatch, escalation, or advisory continuation. Do not infer a reset from feedback prose.
  7. On decline, terminalize the coordinator run as declined.

This design avoids a misleading review experience. Reviewing child diffs independently can miss cross-child interactions. The meaningful artifact is the integrated whole, so the human sees and approves the combined output.

How Merge Actually Happens ​

Merge is deliberately more mechanical than review.

For a standalone run, the merge coordinator:

  1. Canonicalizes and validates the repository path.
  2. Acquires a repository merge lock.
  3. Attempts the transition from awaiting_review or committing to merging; if the CAS loses, it permits an already-merging run while holding the repository lock, otherwise releases the lock and fails (MergeCoordinator.cs:50–62).
  4. Merges the worktree branch into the originating branch, verifying the expected tree hash.
  5. On success, records merged, stores the merge commit hash, and removes the worktree.
  6. On conflict, records merge_failed, stores conflicting files, and preserves the worktree for inspection.
  7. On a retryable blocked outcome or internal fail-safe, reverts back to awaiting_review when possible.

For coordinator assembly, the integration branch is the source, merged into the originating branch. A successful assembly merge terminalizes the coordinator run as completed with an assembly-complete reason rather than as a normal standalone merged run. That difference matters: the parent run represents an orchestration outcome, not a single worker's branch.

Failure Modes and How to Reason About Them ​

Workflow cannot resolve or bind ​

Invalid workflow resolution throws WorkflowBindException; executable nodes must bind to supported runtime behavior. There is no project policy-file discovery or overlay fallback to diagnose.

Reasoning model: a declared gate must execute, not merely appear in configuration.

Review decision races ​

Two clients may submit decisions at nearly the same time. The pending request store and run-status CAS decide one winner. The loser sees conflict/no-pending behavior.

Reasoning model: review is an ownership transfer from waiting workflow to exactly one decision.

Workflow disappears after restart ​

If a run is awaiting review but no live workflow can be resumed, the direct fallback can approve or decline using merge infrastructure. Request-changes is not supported on that direct path because there is no live workflow to resume; the run is restored to awaiting review so the caller can choose approve or decline.

Reasoning model: fallback can finish an irreversible path, but it should not pretend it can reconstruct a revision loop without a workflow.

Merge is blocked ​

A blocked merge can return to awaiting review instead of failing terminally. This lets a human retry once the repository constraint clears.

Reasoning model: "approved" means the user accepted the diff, not that the repository was guaranteed writable at that instant.

Merge conflicts ​

Conflicts become merge_failed, with conflict details and the worktree preserved where applicable.

Reasoning model: conflicts are not safe to auto-resolve under the review approval. The reviewed tree and the target branch no longer compose cleanly.

Coordinator assembly has ineligible children ​

The assembly pipeline blocks before building or merging a partial result.

Reasoning model: collective review is all-or-nothing. A missing or failed child means the integrated output is not the reviewed outcome.

Coordinator review survives a long wait or restart ​

The human assembly gate waits indefinitely until a decision or cancellation. Shutdown leaves in_review recoverable. ResumeInReviewAsync uses the persisted integration branch and tree hash: it applies a persisted decision or re-arms the gate without rebuilding. Only missing/incomplete review metadata falls back to rebuilding (CoordinatorAssemblyService.cs:1321–1385).

Reasoning model: an explicit, durable human wait is not a failed autonomous worker.

Trade-offs ​

  • Declared review vs policy injection. Gate changes belong to workflow definitions; there is no separate configurable safety overlay.
  • Run access vs separate reviewer assignment. Project contributor access and legacy/assembly owner checks are not two-person approval. That would require a separate persisted assignment model and enforcement.
  • Single collective review vs per-child review. Collective review gives a truthful integrated diff. It delays human feedback until fan-in; structured target files and explicit steering limit the scope of rework.
  • Direct fallback vs no fallback. Fallback lets approval/decline complete after some restart scenarios. It intentionally does less than the live workflow to avoid inventing state.
  • Blocked merge returns to review. This keeps runs recoverable, but clients must understand that approval can lead back to an awaiting-review state rather than a terminal result.

Rebuilding Blueprint ​

If you were rebuilding this subsystem from scratch, implement these pieces in order:

  1. Define durable run statuses: in progress, awaiting review, merging, merged, merge failed, declined, completed, failed, and coordinator assembly states.
  2. Implement a workflow request gate that can pause execution and emit a durable review request.
  3. Store pending review requests with owner identity and at-most-once consumption.
  4. Implement review decisions: approve, request changes, decline.
  5. Make request-changes feed sanitized reviewer feedback back into the producer and clear stale approvals/checkpoints.
  6. Declare review gates and their decision edges in validated workflow definitions.
  7. Bind declared gates to executable behavior; fail closed on unsupported bindings.
  8. Implement merge with both a repository lock and run-status CAS; use a distributed lock when multiple API replicas can serve the same project workspace.
  9. Preserve conflict details and recoverable blocked states distinctly.
  10. Trim coordinator child runs so they produce assemble-ready output only.
  11. Resolve applicable authored aggregate gates, persist human review, then merge the integration source into the originating branch and run Scribe.
  12. Route structured request-changes through an explicit steering decision; preserve in-place context where resumable and scope fresh work to implicated subtasks plus dependents.
  13. Persist review/merge events so reload, reconnect, and postmortem inspection see the same story.

The central design principle is simple: agents can propose and revise, automated reviewers can critique, but irreversible repository change passes through explicit review and guarded merge.

Where this lives ​

  • apps/Agentweaver.Api/Endpoints/RunEndpoints.cs
  • apps/Agentweaver.Api/Endpoints/CoordinatorEndpoints.cs
  • apps/Agentweaver.Api/Runs/
  • apps/Agentweaver.Api/Workflows/
  • apps/Agentweaver.Api/Coordinator/
  • packages/Agentweaver.AgentRuntime/Workflow/
  • packages/Agentweaver.Domain/
Diagram details and constraints
ElementContract
titleCollective assembly and review
takeawayRED parks durably for a human. REVISE enters explicit steering, not RaiBlocked.
group-title-0CLAIM AND AGGREGATE
group-title-1AUTHORED CHECKS AND HUMAN WAIT
group-title-2STEERING, RECOVERY AND COMPLETION
Claim + eligibilityClaim + eligibility
Claim + eligibilityNo partial failed plan
Claim + eligibilityawaiting -> assembling
Integration snapshotIntegration snapshot
Integration snapshotOrdered child branches
Integration snapshotbranch / tree / diff
Applicable gatesApplicable gates
Applicable gatesWorkflow-defined ordering
Applicable gatesnon-code: omit build
Gate outcomesGate outcomes
Gate outcomesPass: next; REVISE: steer
Gate outcomesRAI RED: human park
Normal human gateNormal human gate
Normal human gatePersist request, then wait
Normal human gateapprove: next gates
Safety / budget parkSafety / budget park
Safety / budget parkDurable human escalation
Safety / budget parkin_review / awaiting
Explicit steeringExplicit steering
Explicit steeringIn-place, fresh or advisory
Explicit steeringProceed: human park
Recovered reviewRecovered review
Recovered reviewUse saved branch and tree
Recovered reviewno routine rebuild
Approved completionApproved completion
Approved completionLock, merge, then Scribe
Approved completionScribe error: nonfatal
e0eligible
e1snapshot
e2check
e3pass
e4human
e5approve
e6RED
e7REVISE
e8changes
e9Proceed
e10revision
e11recover
e12approved
e13all done
groupsCLAIM AND AGGREGATE; AUTHORED CHECKS AND HUMAN WAIT; STEERING, RECOVERY AND COMPLETION
Diagram details and constraints
ElementContract
titleReview authorizes; merge still guards
takeawayA review-bearing standalone workflow declares its gates; approval alone does not edit Git.
group-title-0WORKFLOW AND CANDIDATE
group-title-1REVIEW ALTERNATIVES
group-title-2CONTINUATION AND GIT RESULT
Selected definitionSelected definition
Selected definitionBind the authored graph
Selected definitionno injected project policy
Producer outputProducer output
Producer outputCapture tree and diff
Producer outputreviewable candidate
Declared review gateDeclared review gate
Declared review gateOnly when workflow includes it
Declared review gatenot universal to all graphs
Request changesRequest changes
Request changesReturn feedback to execution
Request changesrevision path
ApproveApprove
ApproveAllow workflow continuation
Approvenot direct file mutation
DeclineDecline
DeclinePersist declined terminal
Declineno merge authorization
ContinuationContinuation
ContinuationDeliver workflow response
Continuationremaining authored nodes
Merge coordinatorMerge coordinator
Merge coordinatorLock and reviewed-tree guard
Merge coordinatorCAS before Git operation
Actual merge resultActual merge result
Actual merge resultMerged, blocked or conflict
Actual merge resultinternal errors distinct
e0execute
e1review
e2changes
e3approve
e4decline
e5feedback
e6continue
e7on merge
e8result
groupsWORKFLOW AND CANDIDATE; REVIEW ALTERNATIVES; CONTINUATION AND GIT RESULT
Diagram details and constraints
ElementContract
titleGuarded standalone merge
takeawayRepository locking precedes CAS; reviewed-tree mismatch and conflicts are not success.
group-title-0INPUT AND LOCK ADMISSION
group-title-1STATUS GUARD AND GIT
group-title-2OUTCOMES AND RELEASE
Reviewed inputReviewed input
Reviewed inputCanonicalize repository path
Reviewed inputreviewed source + tree
Repository lockRepository lock
Repository lockBounded acquisition wait
Repository lock5-second wait
Repository busyRepository busy
Repository busyNo acquired lock
Repository busyLockFailed
TryStartMerging CASTryStartMerging CAS
TryStartMerging CASReload if CAS loses
TryStartMerging CASalready Merging may proceed
Guarded Git operationGuarded Git operation
Guarded Git operationReviewed tree into origin
Guarded Git operationwhile holding lock
MergedMerged
MergedPersist commit and status
Mergedbest-effort cleanup
Blocked / conflictBlocked / conflict
Blocked / conflictBlocked: restore review
Blocked / conflictconflict: MergeFailed
Internal errorInternal error
Internal errorFiltered exception handling
Internal errorrevert / internal error
Release acquired lockRelease acquired lock
Release acquired lockEvery acquired-lock exit
Release acquired lockfinally
e0validate
e1busy
e2locked
e3allowed
e4merged
e5blocked
e6error
e10denied
groupsINPUT AND LOCK ADMISSION; STATUS GUARD AND GIT; OUTCOMES AND RELEASE
Diagram details and constraints
ElementContract
titleReview API decision paths
takeawayAuthorize first. Deliver through the right path. Lock before any merge CAS.
group-title-0ADMISSION AND REPLAY
group-title-1DELIVERY ALTERNATIVES
group-title-2CONTINUATION AND MERGE
Caller + accessCaller + access
Caller + accessProject contributor check
Caller + accesslegacy: pending owner
Reviewable state?Reviewable state?
Reviewable state?Inspect status + pending
Reviewable state?awaiting_review
Replay or conflictReplay or conflict
Replay or conflictMatching terminal: reuse
Replay or conflictotherwise: 409
Live pendingLive pending
Live pendingChanges / decline use CAS
Live pendingapprove: no merge CAS
Deferred pendingDeferred pending
Deferred pendingPersist the decision first
Deferred pendingthen status transition
No live / no pendingNo live / no pending
No live / no pendingValidate direct approval
No live / no pendingchanges: 409
Consume + deliverConsume + deliver
Consume + deliverSend workflow response
Consume + deliverlive continuation
Repository lockRepository lock
Repository lockOnly on reaching merge
Repository locklock before CAS
Merge CAS + GitMerge CAS + Git
Merge CAS + GitGuard reviewed tree input
Merge CAS + Gitrelease lock on exit
e0check
e1replay
e2live
e3deferred
e4direct
e5deliver
e6on merge
e7approve
e8locked
groupsADMISSION AND REPLAY; DELIVERY ALTERNATIVES; CONTINUATION AND MERGE