# Merging and closeout

[Documentation index](README.md) · [Safety and receipts](2026-10-06-safety-and-receipts.md) · [Workspace operations](2026-10-06-workspace-operations.md)

Plastic merges are workspace/branch operations, not Git fast-forwards. A merge checkin belongs to the loaded target branch; switching afterward does not move or integrate a source-branch checkin.

## Choose a route

| Tool | Use | Boundary |
|---|---|---|
| `plastic_merge` | Apply a noninteractive merge to the loaded workspace | Applying completion and admitted readiness are separate |
| `plastic_mergeToBranch` | Resolve target, switch/update/merge/check readiness/check in | Typed ordered ledger; unknown target/readiness stops before checkin |
| `plastic_mergeBranches` | Workspace-free server-side merge | Same exact requested repository/server; no workspace fallback |

Don't substitute one route for another after failure without a new authorized decision. Command-only preflight does not analyze conflicts; closeout preflight actually performs read-only discovery/pending work.

## merge

The closed producer receipts distinguish applying-command completion from overall outcome/readiness. Core text/JSON derives from the same observation; no version/formatting reads.

| Control | Preserved behavior |
|---|---|
| Merge strategy | Default auto/try uses native conflict markers (CM 11.0.16.10512); source/destination are intentional discard policies |
| Source selectors | Exact branch/changeset/label/shelve spelling, qualified or unqualified |
| Preflight | Zero-command plan, not conflict/readiness analysis |

Strict bounded inputs reject source-as-option/stdin, unsafe Unicode and unknown controls. Source/workdir cap 4,096 code units; aliases keep canonical precedence.

Default auto issues one native marker merge, then marker and independent pending/full-status reads. Exit zero alone is insufficient; applying declaration/action cardinality is no longer required. Exit 1 or unknown/incomplete reads stop without checkin or replay. Output remains bounded external evidence.

See [native markers and intentional bypass](2026-10-23-marker-merges-and-comments.md). Explicit source/destination discard a contributor, never prove preservation. No forced-finalization, backup/restore dance or retry follows uncertainty.

### Admitted readiness profiles

Windows CM admission remains bounded; [observed profiles](2026-10-21-observed-workflow-profiles.md) document independent client captures and limits, not client-family support:

- Explicit contributor-policy routes retain narrow applying parsers; native auto uses completion plus independent state.
- Empty profile: admitted empty machine pending plus exact header-only standard status and no stderr.
- Copied-add profile: bounded CP-only pending records and machine/full-status correlation; up to 128 files under unchanged pending byte/row limits and one merge changeset.
- Resolved merged profile: repeated/mixed replacement, text, copied-add and deletion records correlate structurally with controlled pending paths and source-linked full status. Captured added/deleted directory layouts, bytes/KB/MB and blank size cells are supported. Exit zero and FILE_CONFLICT/DO_MERGE alone never prove resolution; CO/NO_MERGES stops checkin.

Unknown profiles, unrelated controlled work or extra links remain unknown—not zero conflicts or rollback. Explicitly excluded private work must be disjoint; expanded merged partial/private inclusion is unsupported. Forced/cherrypicked policies, moves, directory/binary conflicts, Xlinks and localization remain outside this profile.

`supported-no-unresolved-signals` is point-in-time admitted evidence, not transaction finality, target-head/source-link/exclusivity/other-workspace/Xlink proof. The agent may inspect and choose a supported resolution; the tool doesn't force one.

## mergeToBranch

```text
plastic_mergeToBranch(source="/main/owned-feature", target="/main", message="Merge owned feature", preflight=true)
```

The ordered ledger can include discovery, pending reads, switch, optional update, merge, readiness, checkin and final observations. Inspect stages, outcome, completeness and unattemptedStages before proceeding. Unattempted stages include deliberate omissions as well as stopped steps.

- Source defaults only from directly observed loaded workspace.
- Target defaults only from an admitted source-parent observation.
- Branch inputs are bounded hierarchical selectors; changeset/label/shelve/flag operands are not closeout branches.
- Exact qualified spelling is retained. Different repository/server spellings, including unverified aliases, stop before target mutation.
- Loaded target is independently checked before merge/checkin; final branch mismatch is an error. Unqualified targets can have name-only workspace-context verification, not globally qualified identity proof.
- No child text/fenced JSON is parsed, operation replayed, or raw diagnostic copied as facts.
- A checkin identity remains recorded if final target/pending evidence fails.

### Parent discovery

The installed client filters `name` by leaf but emits full hierarchy in `{name}|{parent}`. One query uses leaf equality, branchname ordering and limit 2 before the optional exact repository clause.

Only one matching full-source/direct-parent row is admitted. Two rows mean ambiguity; empty rows are not proof of branch absence. Warning/failure/malformed/root/unrelated-parent output stops without alternate queries or lexical parent guessing. The discovered target still requires loaded-target verification.

### Investigating unknown readiness

`unsupported_readiness` is not an applying-command failure or rollback receipt. Inspect `commandCompleted`, capture completeness, `protocol` and each stage admission. Diagnostics distinguish unsupported applying output, applying warnings, pending/path correlation and full-status correlation. Stop checkin and do not automatically reapply the merge. Use separately authorized read-only inspection to establish current pending/conflict state; retain sanitized producer evidence for unsupported-profile analysis and verify the package revision actually loaded by the process (a source checkout alone is not proof).

Copied-only admission remains separate from the structurally correlated mixed/deletion route. Synthetic tests prove neither historical recovery nor loaded revision.

### Readiness and message admission

Independent readiness requires complete capture, empty stderr and an admitted clean, copied or resolved-merged profile. Merged closeout re-reads machine pending and correlates resolved rows/source link with the subsequent full loaded-target observation. Copied readiness correlates machine/full status with source, namespace, metadata and paths. Volatile display is opaque. Unknown/duplicate/out-of-scope rows stop; copied and deleted directories require their admitted layouts. Final pending reads never turn failure into zero.

Existing message/card trimming and explicit-message precedence remain. Derived messages must satisfy bounded comment admission before target mutation; interior CR/LF is supported, not other controls or malformed Unicode.

### Defaults and bounds

| Setting / bound | Value |
|---|---|
| Strategy | auto |
| Update target | true |
| Include private | false |
| Pending switch policy | cancel |
| Source / target / workdir controls | 4,096 UTF-16 code units |
| Request | 32,768 UTF-8 bytes |
| Ledger | 16 stages; up to 5 full child receipts and 11 compact reads |
| DTO | 1 MiB UTF-8 bytes |

Preflight is read-only discovery/pending preview, not zero CLI or applying-readiness proof. Selected reads use 64 KiB stdout/16 KiB stderr and nominal 30+5+5-second retirement; child operation limits/recovery rules remain in force. No compound retry/undo/switch-back/strategy change, rollback/exclusivity or guaranteed aggregate deadline is added.

## mergeBranches

This route has no workspace mutation/fallback. Supply distinct fully qualified source/target branches in the same exact requested repository/server. The default `mode="apply"` requires a nonempty message; `mode="analyze"` is read-only remote conflict discovery and rejects a message, resolution file or conflict policy.

```text
plastic_mergeBranches(source="br:/main/source@Example Repository@example@unity", target="br:/main/target@Example Repository@example@unity", message="Merge source", preflight=true)
```

Inputs allow unambiguous `br:/branch@repository@server`, including literal host or full cloud/unity server suffix. Each control caps 4,096 UTF-16 code units. Workdir, unknown controls, malformed Unicode, and `@` inside branch/repository are rejected. Absolute local resolution-input paths are preserved, not trimmed or rewritten.

| Outcome/evidence | Interpretation |
|---|---|
| Preflight | Zero CLI; validates supplied local inputs and renders command intent, not remote conflicts |
| Analyzed | Complete supported point-in-time remote output; no applying command or contributor export |
| Completed | Admitted complete exit emitted one exact target/root changeset; conflict candidates require verified result inputs |
| No-op / ALREADY_CONNECTED | Classification with effect not-proven; not independently verified unchanged state |
| Conflict / unsupported / uncertain / failed | Stop; possible effects are not ruled out |

Applying performs one local help syntax check and at most one noninteractive server merge. Exact decimal ID/emitted server qualifier remain separate from requested server spelling; alias equivalence is unverified.

No source-link/head-exclusive/global/Xlink/rollback proof, retries, head unification or shelveset mode. Undetected server-side Xlink effects remain possible; accepting a full xlink conflict path does not verify linked-repository ownership or effects.

### Agent conflict-resolution workflow

1. **Inspect:** call `mode="analyze"` to execute `cm merge <source> --to=<target>` without `--merge`. Unlike `preflight`, this contacts the server. Read `conflictPaths`, parse/capture completeness and omission counts. Analysis supports the captured three-field `APPLY ADD/RM/FSPROT` and `DIS_OP_WARN CHG` profiles with bounded repository-absolute paths. `parse.warningsObserved` counts supported CHG discard warnings; it does not prove identical contributor content. Empty, malformed, unsupported-operation/subtype (including directory-conflict), other warning or failed output remains uncertain, not a clean merge. Applying has separate terminal/input verification. `analyzed` means a supported observation, not absence of conflicts.
2. **Understand contributors:** independently inspect base/source/destination for each conflict. The exact destination conflict path is usable as a resolution key, not a workspace path. This tool does not retrieve content or assign meanings to the numeric FILE_CONFLICT fields. If needed, use a read-only `cm merge <qualified-source> --to=<qualified-target> --printcontributors` inspection, without `--merge`, to establish contributor identities. Use `plastic_diff(mode="revisions", ...)` for deliberate comparisons after verifying file/repository/server-qualified selectors. For full bytes unavailable through typed tools, follow the [historical export fallback](../skills/using-plastic/references/changeset-operations.md#historical-export-fallback): fresh external temporary destinations only, never canonical workspace files or guessed revision IDs. Writable xlinks need independently verified linked-repository identities; do not qualify their revision IDs against the root repository by assumption.
3. **Resolve and validate:** write merged content into new local result files outside the workspace; preserve immutable contributor baselines. Review the result and run relevant validation before submission. Binary files may use `keep`, but choosing a side deliberately discards the other contributor's conflicting changes. Directory conflicts are not solved by this JSON feature; stop and choose a separately authorized workflow.
4. **Submit:** create a UTF-8 JSON file as below and pass its absolute path as `fileConflictsResolutionsFile` with a merge message. Each `path` must exactly match its observed FILE_CONFLICT destination path, including any writable-xlink prefix. Each entry requires exactly one of `keep` (`source`/`destination`) or an absolute readable regular `resultFile`. Duplicate paths and unknown fields are rejected. Input validation caps JSON at 1 MiB / 500 entries and records its SHA-256 plus counts, not file contents. The resultFile-only candidate-success route additionally hashes bounded canonical regular inputs before help, before dispatch and after completion; see [input verification](2026-10-21-observed-workflow-profiles.md#reviewed-result-file-server-merges).
5. **Observe:** inspect the receipt and any emitted target changeset. Never automatically retry a conflict, timeout or uncertain applying result. Keep source/target heads and all input files unchanged between inspection, validation and submission; these calls provide no transaction, head lock or atomic input snapshot; result-input hashes are not general server-byte proof. If the inputs or heads change, reassess instead of reusing stale merged content. The CLI owns current-conflict matching; its documented invalid-resolution rejection happens before upload, but opaque failures do not establish rollback.

```text
plastic_mergeBranches(source="br:/main/source@Example Repository@example@unity", target="br:/main@Example Repository@example@unity", mode="analyze")
plastic_mergeBranches(source="br:/main/source@Example Repository@example@unity", target="br:/main@Example Repository@example@unity", message="Integrate reviewed resolutions", fileConflictsResolutionsFile="/tmp/resolutions.json")
```

```json
{
  "resolutions": [
    { "path": "/src/Player.cs", "resultFile": "/tmp/merged/Player.cs" },
    { "path": "/art/logo.png", "keep": "source" },
    { "path": "/README.md", "keep": "destination" }
  ]
}
```

The paths above are POSIX examples; on Windows supply native absolute local input paths. The tool and `cm` never change/delete the caller's JSON or result files. The JSON takes precedence over an optional explicit `unlistedConflictPolicy="source"|"destination"`, which emits `--keepsource`/`--keepdestination` only for that intentional fallback. Omit it to keep the existing noninteractive behavior for unlisted conflicts; never select a fallback automatically. `--nointeractiveresolution` is always present, including analysis. The resolution flag is required in local help only when a file is supplied, so ordinary merges retain their existing capability requirements. Bounded Windows live and deterministic evidence is described in [observed profiles](2026-10-21-observed-workflow-profiles.md). Shelve sources/results are not added by this feature.

### Server merge bounds

Stdout 64 KiB/stderr 16 KiB, fatal UTF-8, nullable observed start/terminal/exit facts. Help: 3 second deadline and 32,768 decoded-unit stdout gate (the updated help exceeds the former 16 KiB limit); merge: 30 second deadline with bounded termination escalation. Capture uncertainty never implies cancellation or safe replay.

Classify full framing/records before one aggregate 100-reference projection budget. Unknown/contradictory/malformed/overlimit evidence cannot become success. DTO 131,072 UTF-8 bytes; presentation 24,000 units. Core text/fenced JSON and native text/JSON share one observation; failures return native isError with no diagnostic duplication.

## Follow-up decisions

Inspect merge metadata and pending scope rather than assuming exit 0 permits checkin. Use finalization only with explicit policy intent, not as automatic conflict repair. Manual resolution and any later applying call remain agent/user decisions with their own scope.

For source/consumer/platform limits, see [receipt semantics](2026-10-06-safety-and-receipts.md#evidence-and-transport-limits) and [the workflow skill](../skills/using-plastic/SKILL.md).
