Skip to main content

Merge preview response

The merge preview lists what a branch would change on main if you merged it now. This page documents every field of the REST response:

GET /v1/branches/{id}/merge-preview

The CLI prints its own JSON form of the preview:

tiden branch merge-preview --product-id <product-id> --name <branch-name> --format json

That output is not the REST response. See CLI JSON output for how it differs.

The endpoint uses the usual API authentication. For what the preview means to a reviewer, see Merge preview.

Schema version​

The response carries schemaVersion, an integer. The current version is 3.

  • schemaVersion increases by one whenever a field is added to, or changed in, the response or one of its preview item types: the modification items, the duplicate items, stats, intentSession, blockingSessions, and autoMerge.
  • Existing fields are never renamed, removed, or given a different type within the public API.
  • Each change is listed in Schema history on this page and in the Tiden changelog.
  • The spec, test, and component objects inside the response are shared with the rest of the API. They can gain fields without a change to schemaVersion.

If your client rejects unknown keys, read the REST endpoint, check schemaVersion first, and update the client when the number is higher than the one it was written for.

JSON encoding​

The REST API is generated from the Tiden gRPC API, so the JSON follows the protobuf JSON mapping:

  • Field names are camelCase, such as testAdditions and hasConflict.
  • Fields with an empty or zero value are still sent: an empty list is [], an empty string is "", a zero number is 0, and false is false.
  • An object field with no value is sent as null, for example intentSession on a branch with no intent session.
  • Fields marked optional on this page are left out of the JSON when they have no value.
  • Timestamps are RFC 3339 strings in UTC, such as "2026-10-27T09:00:00Z".

CLI JSON output​

tiden branch merge-preview --format json decodes the REST response and encodes it again. Its output can differ from the REST response:

  • It leaves out fields the CLI does not know. The current CLI leaves out the autoMerge fields gateRed, conflictsResolved, closingFallback, and abandonedAt. An older CLI can leave out more, such as schemaVersion.
  • It leaves out these fields when they are empty or zero: schemaVersion, previewDigest, linkProposals, testDuplicates, stats.testDuplicates, autoMerge, and every field of autoMerge except state.
  • Spec, test, component, and session objects pass through as the server sent them.

For a check that rejects unknown or missing keys, read the REST endpoint.

Top-level fields​

In the API, a spec is the requirement resource, so spec changes use the plain names additions, modifications, and deletions.

FieldTypeMeaning
schemaVersionintegerVersion of this response shape. See Schema version.
previewDigeststringFingerprint of what the branch would put into main: 64 lowercase hex characters (SHA-256). See Merge only the reviewed preview.
additionslist of specSpecs the branch created.
modificationslist of modification of specSpecs the branch changed.
deletionslist of specSpecs the branch deleted, as they are on main.
testAdditionslist of testSuites and cases the branch created, without the duplicates.
testModificationslist of modification of testSuites and cases the branch changed.
testDeletionslist of testSuites and cases the branch deleted.
testDuplicateslist of duplicateSuites and cases the branch created that already exist on main. The merge still adds them.
componentAdditionslist of componentComponents the branch created.
componentModificationslist of modification of componentComponents the branch changed.
componentDeletionslist of componentComponents the branch deleted.
statsstatsCounts of the lists above and of conflicts.
intentSessionsession or nullThe branch's intent session state and its verdict. See Intent sessions for which session each field describes. null when the branch has no intent session.
blockingSessionslist of sessionSessions on the branch whose specs were not written down yet. These are the sessions that can stop the merge.
hasRequirementChangesbooleantrue when the branch holds spec changes, counted the way the merge check counts them.
linkProposalslist of link proposalTest-to-spec link proposals that the merge makes into links on main: those with the status proposed or accepted. Rejected proposals are not listed.
autoMergeauto-mergeOptional. Where the automatic merge of an intent branch stands. Left out for a branch that Tiden does not track for automatic merge.

Lists that hold no items are []. A branch with no test or component changes has empty test and component lists.

Item shapes by kind of change​

The items in the lists do not share one shape. Each kind of change has its own:

Kind of changeListsItem shape
Additionadditions, testAdditions, componentAdditionsThe full entity object: a spec, test, or component.
Deletiondeletions, testDeletions, componentDeletionsThe full entity object, as above.
Modificationmodifications, testModifications, componentModificationsbranchVersion, mainVersion, hasConflict, conflictingFields.
DuplicatetestDuplicatesbranchVersion, mainVersion, match.

Modification items​

A modification item holds both versions of one spec, test, or component.

FieldTypeMeaning
branchVersionentity objectThe item as the branch has it. This is the version the merge writes.
mainVersionentity objectThe item as main has it now.
hasConflictbooleantrue when main changed this item after the branch copied it. See Conflict resolution.
conflictingFieldslist of stringsNames of the fields whose values differ between the two versions, in snake_case, such as title, parent_id, or component_id. A spec whose decisions conflict also lists decisions:<spec-id>.

Duplicate items​

A duplicate item is a suite or case the branch created that already exists on main.

FieldTypeMeaning
branchVersiontestThe suite or case the branch created.
mainVersiontestThe suite or case on main that it matches.
matchstringHow it matched. suite_path: a suite with the same title path from the root. signature: a case with the same signature.

A link proposal is a test-to-spec link that the branch proposes. The merge makes it a link on main.

FieldTypeMeaning
idstringID of the proposal.
testIdstringID of the test.
requirementIdstringID of the spec.
testSeqNumintegerSequence number of the test.
requirementSeqNumintegerSequence number of the spec.
statusstringproposed or accepted.

Spec​

A spec object, the requirement resource of the API. It can gain fields without a change to schemaVersion.

FieldTypeMeaning
idstringSpec ID.
productIdstringProduct ID.
parentIdstringOptional. Parent spec in the tree. Left out for a top-level spec.
componentIdstringOptional. Component the spec belongs to.
titlestringTitle.
contentstringBody, in Markdown.
positionintegerOrder among its siblings.
createdBystringID of the user who created it.
childrenCountintegerNumber of direct child specs.
createdAttimestampWhen it was created.
updatedAttimestampWhen it last changed.
seqNumintegerProduct-wide number used in references such as PRJ-12.
statusstringSpec status.
prioritystringSpec priority.
assigneeIdstringOptional. Assigned user.
branchIdstringBranch that holds this copy.
sourceIdstringOptional. The main spec this branch copy was made from. Left out for a spec the branch created.
branchStatusstring"", added, modified, or unchanged.
typestringSpec type.
sourceCountintegerNumber of sources.
sourceslist of objectsWhere the spec came from: repository file anchors, documentation URLs, and similar records. Can be empty; sourceCount has the count.
derivedStatestring"" or machine_unpromoted: Tiden wrote the spec from merged code and nobody has directed work on it yet. Cleared when a linked test runs or an intent session settles against the spec.
decisionslist of objectsThe spec's decision records. Can be empty; decisionCount has the count.
decisionCountintegerNumber of decision records.

Test​

A test object is a suite or a case; kind tells which. Case-only fields are zero or empty on a suite. It can gain fields without a change to schemaVersion.

FieldTypeMeaning
idstringTest ID.
productIdstringProduct ID.
branchIdstringBranch that holds this copy.
parentIdstringOptional. Parent suite. Left out at the root.
kindstringsuite or case.
titlestringTitle.
descriptionstringDescription.
positionintegerOrder among its siblings.
seqNumintegerOptional. Case number. Left out for a suite.
statusstringCase status.
prioritystringCase priority.
typestringCase type.
layerstringTest layer.
mutedbooleantrue when the case is muted.
componentIdstringOptional. Component the case belongs to.
assigneeIdstringOptional. Assigned user.
tagslist of stringsTags.
customFieldsobjectCustom field values, as a JSON object.
stepslist of objectsThe case's steps. A step can hold child steps.
originstringmanual or imported.
frameworkstringTest framework of an imported test.
externalIdstringID of the test in the source it was imported from.
externalPathstringPath of the test in that source.
filePathstringRepository file of the test.
lastSyncedAttimestamp or nullWhen the import last updated it. null for a test that was never imported.
sourceIdstringOptional. The main test this branch copy was made from.
branchStatusstring"", added, modified, or unchanged.
childrenCountintegerNumber of direct children, suites and cases.
directCaseCountintegerSuite only. Number of direct child cases.
directSuiteCountintegerSuite only. Number of direct child suites.
descendantCaseCountintegerSuite only. Number of cases at any depth below it.
linkedRequirementCountintegerCase only. Number of specs linked to the case.
createdBystringID of the user who created it.
createdAttimestampWhen it was created.
updatedAttimestampWhen it last changed.
isAutomatedbooleantrue when the case is automated.
signaturestringCase identity from a reporter. See Signature.
testopsIdintegerOptional. TestOps case ID linked to the case. Left out when none is set.
authorTypestringuser, agent, reporter, import, system, or unknown.
authorIdstringID of the author.
authorNamestringName of the author.
parameterGroupslist of objectsParameter sets the case runs with.
latestExecutionobject or nullThe latest reported result: status, duration, and timing.
attachmentslist of stringsAttachments.
relationslist of objectsRelations to other tests.
sourceShastringGit blob SHA of the source the test was last imported from. Empty for tests not imported from a repository snapshot.
latestExecutionStalebooleantrue when latestExecution ran against an earlier sourceSha than the current one.
repositorystringRepository of the case. Empty when unknown.
mergedIntoIdstringOptional. Set while this case is being merged, or has been merged, into another case.
redirectedFromstringSet only by a single-test read that was redirected to a merged case. Empty here.
keyslist of objectsIdentity keys of the case. Filled only by a single-test read. Empty here.

Component​

A component object. It can gain fields without a change to schemaVersion.

FieldTypeMeaning
idstringComponent ID.
productIdstringProduct ID.
namestringName.
descriptionstringDescription.
createdAttimestampWhen it was created.
updatedAttimestampWhen it last changed.
branchIdstringBranch that holds this copy.
sourceIdstringOptional. The main component this branch copy was made from.
branchStatusstring"", added, modified, or unchanged.
repositorystringOptional. Canonical repository ID, such as github.com/acme/backend. Left out for a component with no repository.
componentPathslist of stringsPath prefixes inside the repository. Empty means the whole repository.
repositoryAliaseslist of stringsLocal checkout paths that map to the repository.
runtimePathslist of stringsPath prefixes that error stack frames show at runtime.

Stats​

stats counts the lists above.

FieldTypeMeaning
additionsintegerSpec additions.
modificationsintegerSpec modifications.
deletionsintegerSpec deletions.
testAdditionsintegerTest additions, duplicates included, because the merge adds them. It equals the length of testAdditions plus the length of testDuplicates.
testModificationsintegerTest modifications.
testDeletionsintegerTest deletions.
testDuplicatesintegerHow many of the test additions are duplicates.
componentAdditionsintegerComponent additions.
componentModificationsintegerComponent modifications.
componentDeletionsintegerComponent deletions.
conflictsintegerModification items with hasConflict set, over specs, tests, and components together.

Intent sessions​

intentSession and each row of blockingSessions have the same shape. An intent session is the record of one coding agent session on the branch.

Each blockingSessions row describes one session. The fields of intentSession can come from two sessions:

  • sessionId, status, and settled describe the latest session on the branch.
  • agent, objective, gitRepo, and verdict describe the latest session that is not a Tiden system round. A system round is a session that Tiden runs itself, such as the closing pass or a checkpoint merge round. Its agent is tiden and it never carries a verdict. verdict comes from the latest such session that has been graded.
FieldTypeMeaning
sessionIdstringSession ID.
statusstringprovisional, materialized, or closed.
settledbooleantrue when a settlement is recorded for the session. A settlement says which specs the session wrote, or that it had nothing to write down. A closed session can still be unsettled.
agentstringThe coding agent that ran the session.
objectivestringThe objective the session recorded when it started.
gitRepostringThe git repository the session worked in.
verdictstringFilled on intentSession only. The key is also present on every blockingSessions row, always ""; read intentSession.verdict. How the files the session changed compare with the files anchored to the specs the session retrieved when it started. A session is graded when the agent reconciles it during the session (tiden intent reconcile) and when it closes, so an open session can carry a verdict. The latest grading wins. Empty until the first grading.

Verdict values:

ValueMeaning
confirmEvery changed file is among the files anchored to the specs the session retrieved at start, or the session changed no files.
amendSome changed files are outside those anchored files, or the start retrieved no anchored files.
flag-divergenceNo changed file is among those anchored files.
""The session has not been graded yet.

When the verdict is amend or flag-divergence, tiden branch merge-preview prints a warning on stderr, in text and in JSON mode, whatever the session status:

intent session verdict is "amend": the session changed files outside the files anchored to the specs it retrieved at start; review the changes before you merge

The JSON on stdout does not change. Review the changes outside those files before you merge.

blockingSessions lists the sessions that the merge check looks at: open sessions, and sessions closed as abandoned, that recorded no settlement. settled is false on every row. The merge refuses with INTENT_UNDISTILLED when hasRequirementChanges is true and blockingSessions is not empty. Use these two fields to predict that result. Do not compute it from the lengths of additions, modifications, and deletions: those lists leave out copies that do not differ from main, so they can disagree with the check.

Auto-merge​

autoMerge describes the automatic merge of an intent branch. See Automatic merge of intent branches.

FieldTypeMeaning
statestringWhere the branch is in the automatic merge. Values are below.
reasonstringCode for why the branch waits. Empty when nothing stops it. Values are below.
reasonDetailstringText that goes with reason.
nextCheckAttimestamp or nullWhen Tiden looks at the intent next: the time the merge box shows as "merges automatically after".
holdUntiltimestamp or nullEnd of the hold. It stays set after the hold ends, so a hold is active only while this time is in the future.
holdReasonstringReason given for the hold.
heldByNamestringWho set the hold.
holdSourcestringagent or person.
openPromiseslist of stringsTexts of the promises still open, for a partly delivered intent.
partialUntiltimestamp or nullWhen a partly delivered intent merges its shipped part as a checkpoint.
successorBranchIdstringThe intent branch that carries the open promises after a checkpoint merge.
successorBranchNamestringName of that branch.
gateRedbooleanThe branch Quality Gate was Blocked, or could not be computed, when Tiden merged.
conflictsResolvedintegerNumber of conflicts the automatic merge resolved.
closingFallbackbooleanThe branch merged without the closing pass.
abandonedAttimestamp or nullWhen Tiden closed the intent as abandoned. Set while state is abandoned.

state values:

ValueMeaning
in_progressWork on the intent is still going on.
countdownThe conditions are met and Tiden merges after nextCheckAt.
closingThe closing pass is running.
partialPromises are still open. See openPromises and partialUntil.
mergedTiden merged the branch.
checkpoint_mergedTiden merged the shipped part and moved the open promises to the successor branch.
abandonedTiden closed the intent as abandoned.
offTiden does not merge this branch by itself.

reason codes:

ValueMeaning
product_setting_offAutomatic intent merge is off for the product.
branch_not_openThe branch is not open.
not_intent_branchThe branch is not an intent branch.
on_holdA hold is active.
pr_openA linked pull request is still open.
pr_state_unknownTiden cannot read the state of a linked pull request.
round_activeAn agent session is still working on the intent.
no_merged_prNo linked pull request has merged.
quiet_periodThe 24 hours without activity have not passed.
closing_passThe closing pass is running.
promise_openA promise of the intent is still open.
no_acting_userNo member with access can be the actor of the merge.
merge_failedThe last merge attempt failed.
closing_conflictsThe closing pass found conflicts it has to settle.
changed_during_mergeThe intent or main changed while Tiden was merging it, for example a spec conflict that the preview did not show. The merge did not go ahead; Tiden checks the intent again at once.

New codes can appear. Show reasonDetail for a code your client does not know.

Merge only the reviewed preview​

previewDigest covers the branch side of the merge: its specs and their decisions, tests, components, deletions, and link proposals. It changes when any of these change on the branch. It does not change when only main moves, and it holds no timestamps or session state.

To merge only what you reviewed, send the digest back with the merge:

tiden branch merge \
--product-id <product-id> \
--name <branch-name> \
--expect-preview <digest>

Over REST, send it as expectPreviewDigest in the body of POST /v1/branches/{id}/merge. When it differs from the branch's current digest, Tiden refuses the merge with 409 Conflict and a message that starts with MERGE_PREVIEW_STALE:. Nothing is merged. Run the merge preview again, review it, and merge with its new digest.

A merge without the digest does not do this check. With --auto-resolve, the CLI reads a fresh preview first and stops with exit code 3 when its digest differs from --expect-preview.

Example​

A branch that adds one spec and has a closed intent session with the verdict amend. The spec object is shortened here; the API sends every field listed in Spec.

{
"schemaVersion": 3,
"previewDigest": "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c",
"additions": [
{
"id": "7c1e2a4b-0d3f-4b8e-9a51-2f6c8d0e1b37",
"title": "Export works offline",
"branchStatus": "added"
}
],
"modifications": [],
"deletions": [],
"stats": {
"additions": 1,
"modifications": 0,
"deletions": 0,
"conflicts": 0,
"testAdditions": 0,
"testModifications": 0,
"testDeletions": 0,
"componentAdditions": 0,
"componentModifications": 0,
"componentDeletions": 0,
"testDuplicates": 0
},
"testAdditions": [],
"testModifications": [],
"testDeletions": [],
"componentAdditions": [],
"componentModifications": [],
"componentDeletions": [],
"intentSession": {
"sessionId": "5b0d9e62-3c71-4f2a-8e19-a7d4c6b2f803",
"status": "closed",
"settled": true,
"agent": "claude-code",
"gitRepo": "github.com/acme/backend",
"objective": "Add offline export",
"verdict": "amend"
},
"blockingSessions": [],
"hasRequirementChanges": true,
"testDuplicates": [],
"linkProposals": []
}

autoMerge is left out because this branch has no automatic merge record.

Schema history​

VersionDateChange
12026-10-27First published version. Adds schemaVersion, intentSession.verdict, and blockingSessions[].verdict (always ""; read intentSession.verdict). intentSession now fills agent, objective, and gitRepo.
22026-10-29Adds linkProposals.
32026-10-30Adds previewDigest. Send it as expectPreviewDigest to merge only the reviewed preview.