Merging stacks
A stack merges bottom-first. You can land the whole stack or any contiguous prefix of it — layers 1 and 2 while layer 3 is still in review — but never a layer whose foundations have not landed.
Merging requires write access, and merge policy is evaluated against the trunk for every layer. A layer's direct base being an intermediate branch never relaxes what the trunk requires.
Merge a prefix in one operation
POST /api/v3/repos/:owner/:repo/stacks/:stack_number/merge lands a
contiguous prefix as one operation:
curl -X POST \
-H "Authorization: Bearer $OPENAGENTS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"pull_request_number": 118, "merge_method": "merge"}' \
https://openagents.com/api/v3/repos/acme/api/stacks/1/merge
pull_request_numbernames the highest layer to land. The operation merges the contiguous prefix from the bottom of the stack through that layer.merge_methodismerge,squash, orrebase.expected_stack_versionand expected head OIDs are optional guards; when supplied, a stack that moved since you read it fails the merge instead of landing something you did not review.
The request returns 202 Accepted with a durable operation. Poll GET .../stacks/:stack_number/operations/:operation_id until it succeeds or
fails. The trunk moves once for the whole prefix. Merged pull requests close as
merged, their entries leave the stack, and the layers above become the new
bottom.
Merge one layer asynchronously
PUT /api/v3/repos/:owner/:repo/pulls/:pull_number/merge-async submits a
merge against one stacked pull request — it lands the contiguous prefix from
the bottom of the stack through that layer — and returns 202 Accepted
immediately with an operation_id and a poll URL:
curl -X PUT \
-H "Authorization: Bearer $OPENAGENTS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"merge_method": "squash"}' \
https://openagents.com/api/v3/repos/acme/api/pulls/117/merge-async
Poll GET .../pulls/:pull_number/merge-async/:operation_id for the outcome.
merge_status is pending while the operation runs, then merged or
failed. merge_method defaults to merge. Repeating the request with the
same Idempotency-Key and body replays the same operation instead of
starting a second one; a different active operation on the stack returns
409 Conflict with that operation's id.
The submission is validated when it executes, not when it is accepted: branch heads, stack membership, and merge policy are all checked at execution time. An unstacked pull request cannot merge through this surface.
Automatic restack after a partial merge
Landing a prefix rewrites the foundation under the remaining layers, so a partial merge restacks them automatically: the remaining branches are rebased onto the new trunk tip in the same coordinated fashion as Rebase the stack, and the stack's version advances. The layers left behind stay reviewable without a manual cleanup step, and a restack that would conflict fails the merge during planning — it never lands the prefix and strands the rest.
What atomic means here
Within one merge operation, the trunk either gains the whole requested prefix
or none of it — a conflict or policy failure partway lands nothing. It does
not mean the trunk pauses for you: another merge can land between your read
and your merge, which is what the expected_stack_version guard is for.