Changes and Delivery
ChangeSets
Section titled “ChangeSets”A ChangeSet is an immutable snapshot of what changed in a Worktree.
- Capture runs as the
changeset.captureJob. It starts automatically after a successful Turn in developer, integration or coordinator Sessions that have a repository, or on request withPOST /api/sessions/{id}/changesets. A capture whose source Turn failed, was cancelled or interrupted is recorded with originsalvageand is not eligible for automatic delivery. - The runtime writes the exact tree and reports a manifest, per-file SHA-256
digests, the patch and file blobs. Credential files such as
.env,auth.jsonand private keys are excluded; templates such as.env.exampleare preserved. Selected credentials and obvious secret patterns fail capture. - A Turn completes only after its managed CLI descendants stop. Capture and checkpoint close managed terminals and stop services before copying state. An unconfirmed writer stop blocks the operation.
- Before sealing, the control plane rebuilds the manifest from its own pinned
repository and base commit, recomputes
subject_digestand re-verifies every blob. A failed capture isfailed; there is never a fake ready subject. - States include
readyandfailed. A sealed ChangeSet cannot be modified.
cs = client.changesets.wait_ready(session_id, source_turn_id=turn["id"])print(client.changesets.diff(cs["id"]))GET /api/sessions/{id}/changes shows live, uncaptured changes without waking
compute. POST /api/changesets/{id}/applications applies a ChangeSet to another
Session only if its working tree equals the ChangeSet baseline; otherwise nothing
is written.
Delivery
Section titled “Delivery”POST /api/changesets/{id}/deliveries requests delivery of exactly that
ChangeSet. The Delivery Job performs three steps and records each as evidence:
- Materialize: fetch the pinned base, apply the patch, verify the tree equals the sealed tree, and create a deterministic commit.
- Push to
sbx/SESSION/CHANGESETusing--force-with-lease. If the remote branch holds a different head the Delivery blocks withremote_head_changed; there is no plain force-push. - Pull request: find the existing PR for the head ref or open a draft PR, then verify its head equals the commit.
Use retries for a same-intent transport retry and refreshes to reconcile
with the remote. delivery_unresolved means the remote result could not be
proven yet.
Merge gate
Section titled “Merge gate”Merge is a separate operation, POST /api/deliveries/{id}/merge-requests. The
merge worker reloads the current Project policy and enforces it alongside the
Delivery’s pinned policy and a fresh remote observation. Tightening a policy
also constrains existing Deliveries. All of the following must hold:
- The ChangeSet is sealed, the Delivery is verified, and the remote head equals
the mapped commit equals
expected_head_sha. - The expected Delivery
versionmatches and the merge method is allowed. - Every required DelegationResult is pinned to this exact
subject_digest, comes from an independent child Session and is valid; norequest_changesresult exists for this subject. - Required checks pass on the exact head and the PR is open. A draft PR merges
only if the request sets
mark_ready. require_base_unchangedis unsupported, so a policy that needs it blocks.
Otherwise the call fails with gate_blocked and reasons. The provider merge is
called with sha=expected_head_sha, so a head that moved after the check is
refused by the provider too. A new ChangeSet is a new subject: earlier approvals
do not carry over (stale_subject).
The SDK sends the server’s pins for you:
delivery = client.deliveries.wait(client.deliveries.request(cs["id"])["id"])client.deliveries.merge(delivery["id"], method="squash")Humans cannot post verdicts. Review and test results come only from child Sessions.