Deterministic Three-Way Merge for JSON Values
draft-jurkovikj-json-three-way-merge-00
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Author | Antun Jurkovikj | ||
| Last updated | 2026-08-04 | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Additional resources |
Demo source, vectors, and tests
Interactive browser demonstration libdualnative v0.1.0-alpha.1 reference implementation |
||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-jurkovikj-json-three-way-merge-00
Network Working Group A. Jurkovikj
Internet-Draft 4 August 2026
Intended status: Experimental
Expires: 5 February 2027
Deterministic Three-Way Merge for JSON Values
draft-jurkovikj-json-three-way-merge-00
Abstract
For a fixed, disclosed resource policy, this document defines a
deterministic three-way merge operation for a restricted JSON value
domain. Given a shared base value and two independently derived
values, called source and target, the operation produces either one
complete merged JSON value or an ordered set of structured conflicts.
The operation defines strict JSON input processing, finite binary64
number normalization, scalar and object merge laws, explicit missing-
member semantics, RFC 6901 conflict paths, typed conflict kinds, a
fail-closed result for arrays, and bounded failure behavior. It is
independent of HTTP and does not define array merge semantics,
application-specific semantic resolution, content identity, or
authorization policy.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 5 February 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
Jurkovikj Expires 5 February 2027 [Page 1]
Internet-Draft JSON Three-Way Merge August 2026
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Goals . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2. Non-Goals . . . . . . . . . . . . . . . . . . . . . . . . 4
2. Relationship to Existing JSON Standards . . . . . . . . . . . 5
3. Conventions and Terminology . . . . . . . . . . . . . . . . . 6
4. Input Domain and Normalization . . . . . . . . . . . . . . . 7
4.1. One Complete JSON Value . . . . . . . . . . . . . . . . . 7
4.2. Binary64 Number Model . . . . . . . . . . . . . . . . . . 7
4.3. Merge Value Kinds . . . . . . . . . . . . . . . . . . . . 8
4.4. Normalized Equality and Kind . . . . . . . . . . . . . . 8
5. Validation and Failure Precedence . . . . . . . . . . . . . . 9
5.1. Input-Side Order . . . . . . . . . . . . . . . . . . . . 9
5.2. Within One Input Side . . . . . . . . . . . . . . . . . . 9
5.3. Unsupported Arrays . . . . . . . . . . . . . . . . . . . 10
5.4. Invalid Input . . . . . . . . . . . . . . . . . . . . . . 11
5.5. Resource Policy . . . . . . . . . . . . . . . . . . . . . 11
6. Deterministic Merge Operation . . . . . . . . . . . . . . . . 14
6.1. Equality Shortcuts and Merge Laws . . . . . . . . . . . . 14
6.2. Scalar and Type Merge . . . . . . . . . . . . . . . . . . 15
6.3. Object Merge . . . . . . . . . . . . . . . . . . . . . . 15
6.4. Object-Member Presence Matrix . . . . . . . . . . . . . . 16
6.5. No Partial Result . . . . . . . . . . . . . . . . . . . . 17
7. Conflicts and Diagnostic Ordering . . . . . . . . . . . . . . 17
7.1. Conflict Kinds . . . . . . . . . . . . . . . . . . . . . 17
7.2. Presence Encoding . . . . . . . . . . . . . . . . . . . . 18
7.3. Path and Conflict Ordering . . . . . . . . . . . . . . . 18
8. JSON Result Form . . . . . . . . . . . . . . . . . . . . . . 19
8.1. Merged . . . . . . . . . . . . . . . . . . . . . . . . . 19
8.2. Conflict . . . . . . . . . . . . . . . . . . . . . . . . 20
8.3. Unsupported . . . . . . . . . . . . . . . . . . . . . . . 20
8.4. Invalid Input . . . . . . . . . . . . . . . . . . . . . . 20
8.5. Resource Limit . . . . . . . . . . . . . . . . . . . . . 20
9. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 21
9.1. Independent Object Changes . . . . . . . . . . . . . . . 21
9.2. Missing Is Not Null . . . . . . . . . . . . . . . . . . . 22
9.3. Delete Versus Edit . . . . . . . . . . . . . . . . . . . 22
9.4. Divergent Additions Are Conservative . . . . . . . . . . 22
Jurkovikj Expires 5 February 2027 [Page 2]
Internet-Draft JSON Three-Way Merge August 2026
9.5. Binary64 Equality . . . . . . . . . . . . . . . . . . . . 23
10. Informative HTTP Reconciliation Example . . . . . . . . . . . 23
11. Security Considerations . . . . . . . . . . . . . . . . . . . 23
11.1. Resource Exhaustion . . . . . . . . . . . . . . . . . . 23
11.2. Conflict-Value Disclosure . . . . . . . . . . . . . . . 24
11.3. Merge Success Is Not Semantic Safety . . . . . . . . . . 24
11.4. Base Authenticity and Current State . . . . . . . . . . 24
11.5. Binary64 Rounding . . . . . . . . . . . . . . . . . . . 24
11.6. Unicode and Path Presentation . . . . . . . . . . . . . 25
11.7. Unsupported Arrays . . . . . . . . . . . . . . . . . . . 25
12. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 25
13. Implementation Status . . . . . . . . . . . . . . . . . . . . 25
14. References . . . . . . . . . . . . . . . . . . . . . . . . . 26
14.1. Normative References . . . . . . . . . . . . . . . . . . 26
14.2. Informative References . . . . . . . . . . . . . . . . . 26
Appendix A. Merge Pseudocode . . . . . . . . . . . . . . . . . . 27
Appendix B. Conformance Vectors . . . . . . . . . . . . . . . . 28
B.1. Successful Merge Vectors . . . . . . . . . . . . . . . . 29
B.2. Conflict Vectors . . . . . . . . . . . . . . . . . . . . 30
B.3. Unsupported-Array Vectors . . . . . . . . . . . . . . . . 32
B.4. Invalid-Input Precedence Vectors . . . . . . . . . . . . 32
Appendix C. Design Rationale . . . . . . . . . . . . . . . . . . 32
C.1. Why Arrays Fail Closed . . . . . . . . . . . . . . . . . 33
C.2. Why Divergent Additions Do Not Recurse . . . . . . . . . 33
C.3. Why Merge Is Value-Deterministic Rather Than
Byte-Canonical . . . . . . . . . . . . . . . . . . . . . 33
C.4. Why UTF-16 Code-Unit Ordering . . . . . . . . . . . . . . 33
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 33
1. Introduction
Systems commonly retain a JSON value, allow two parties to edit
copies of that value independently, and later need to reconcile both
edits. Existing standardized JSON patch formats describe a change to
one target value, but do not define how to reconcile two complete
edited values derived from one shared base.
Without a common operation, implementations often use textual merge,
last-writer-wins replacement, or application-specific rules. Those
approaches can produce different outcomes across runtimes or silently
discard one party's change.
This document defines an implementation-independent three-way merge
operation:
Jurkovikj Expires 5 February 2027 [Page 3]
Internet-Draft JSON Three-Way Merge August 2026
Merge(base, source, target)
-> merged value
| ordered conflicts
| typed input, unsupported-domain, or resource failure
The operation is deliberately conservative. It merges normalized
scalar values and recursively array-free objects. Arrays are outside
the initial merge domain and cause a typed fail-closed result even
when all three arrays are equal. A future specification can define
array behavior under a separate semantic identity and separate
conformance vectors.
1.1. Goals
This document provides:
* deterministic normalized equality for JSON values in the merge
domain;
* merge laws that preserve one-sided and agreed changes;
* recursive combination of independent object-member changes;
* explicit distinction between a missing member and a present JSON
null;
* conflicts for divergent edits, incompatible type changes,
divergent additions, and edit-versus-delete cases;
* unambiguous RFC 6901 paths and deterministic conflict ordering;
and
* bounded, complete outcomes without partial merged values or
conflict prefixes.
1.2. Non-Goals
This document does not define:
* array merge, sequence alignment, keyed collections, or reordering
rules;
* a JSON diff or patch-generation algorithm;
* a CRDT, operational transformation, consensus, or replication
protocol;
Jurkovikj Expires 5 February 2027 [Page 4]
Internet-Draft JSON Three-Way Merge August 2026
* application-specific semantic merge or automatic conflict
resolution;
* provenance receipts, redaction formats, or migration between merge
engines;
* JSON canonicalization or content identifiers as a merge
prerequisite;
* an HTTP method, field, status code, Link relation, or media type;
* persistence, atomic commit, authentication, authorization, or
licensing; or
* a determination that a structurally merged value is valid for an
application's schema or policy.
2. Relationship to Existing JSON Standards
JSON Patch [RFC6902] expresses an ordered sequence of operations to
apply to one target document. JSON Merge Patch [RFC7396] expresses,
by example, a patch to apply to one target value. Both are useful
mutation formats, but neither takes a shared base and two
independently edited complete values or defines the conflicts
specified here.
JSON Pointer [RFC6901] defines the path syntax used by this document.
JSON [RFC8259] defines the underlying interchange syntax and data
model, subject to the additional input and number restrictions in
this document.
Internet JSON (I-JSON) [RFC7493] has related interoperability goals.
Both I-JSON and this document require UTF-8, reject duplicate decoded
object names, exclude lone surrogates, and account for binary64
interoperability. This document is not an I-JSON profile, however:
it recursively excludes arrays, normatively converts admitted numbers
to finite binary64 values, and does not import every I-JSON
restriction, including its prohibition on Unicode noncharacters.
Jurkovikj Expires 5 February 2027 [Page 5]
Internet-Draft JSON Three-Way Merge August 2026
The JSON Canonicalization Scheme (JCS) [RFC8785] defines
deterministic JSON serialization for hashing and signing. This merge
operation is deterministic at the normalized JSON-value and ordered-
diagnostic levels and does not require JCS, a digest, or a content
identifier. An application that independently needs deterministic
bytes can apply a compatible canonicalization contract after a
successful merge. The unsigned UTF-16 comparator used here for one
decoded pointer token aligns with JCS property-name sorting, but this
document's ordering of complete pointer-token sequences is separate
and does not make JCS a merge prerequisite.
3. Conventions and Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
*Base* The shared JSON value from which source and target were
independently derived.
*Source* One edited JSON value derived from the base. The name does
not grant this side authority over target.
*Target* The other edited JSON value derived from the base. The
name does not grant this side authority over source.
*Normalized value* A value admitted by the input-domain rules
(Section 4), with every number represented by its normalized
finite binary64 value.
*Present* An object member exists and has a JSON value, including
JSON null.
*Missing* An object member does not exist. Missing is an operation-
internal state and is not a seventh JSON value kind.
The base, source, and target root values are always present. Missing
can arise only while processing an object member.
*Resource-policy identifier* A stable identifier for one fixed set
of resource ceilings, deterministic measurement encodings, and any
policy-defined limit precedence. The generic JSON result form
carries this identifier in error.budget.
*Merge processor* An implementation of the operation defined by this
document.
Jurkovikj Expires 5 February 2027 [Page 6]
Internet-Draft JSON Three-Way Merge August 2026
In pseudocode, B, S, and T denote base, source, and target values.
M(B,S,T) denotes the merge operation. == denotes normalized equality
as defined in Normalized Equality and Kind (Section 4.4), not textual
equality of input JSON.
4. Input Domain and Normalization
4.1. One Complete JSON Value
Each input is a UTF-8 sequence containing exactly one JSON value as
defined by [RFC8259]. Leading and trailing JSON whitespace are
permitted. After the value and trailing whitespace, any additional
octet is invalid.
A leading UTF-8 byte order mark (the octet sequence encoding U+FEFF)
MUST be rejected with code invalid_json. This profile does not use
the permissive RFC 8259 option to ignore a byte order mark while
parsing.
A merge processor MUST reject malformed JSON, invalid UTF-8, and
escaped or unescaped lone surrogate code points. It MUST preserve
admitted Unicode scalar values exactly and MUST NOT apply Unicode
normalization.
An object MUST NOT contain the same decoded member name more than
once. Escape spelling does not make names distinct; for example, "a"
and "\u0061" are duplicates in the same object and MUST be rejected
before merge processing.
Many general-purpose JSON parsers silently retain only the first or
last occurrence of a duplicate member name. A processor cannot
satisfy the requirement above by inspecting a post-parse object after
such a collapse. It MUST use a parser mode or input front end that
exposes every decoded member-name occurrence or rejects duplicates
before collapsing them.
4.2. Binary64 Number Model
Every JSON number token MUST be converted to the nearest IEEE 754
binary64 value [IEEE754] using round-to-nearest, ties-to-even. A
token whose conversion overflows to positive or negative infinity
MUST be rejected. NaN and infinity are not JSON number tokens and
are invalid input.
All finite binary64 values, including subnormal values, are admitted.
Positive and negative zero compare equal and are normalized to
positive zero. Thus, the input tokens -0, 0, and 0.0 denote the same
normalized number.
Jurkovikj Expires 5 February 2027 [Page 7]
Internet-Draft JSON Three-Way Merge August 2026
Different decimal tokens that convert to the same binary64 value are
equal. For example, 1, 1.0, and 1e0 are equal. The token
9007199254740993 converts to the binary64 value also denoted by
9007199254740992; those tokens therefore compare equal under this
profile. Applications that require exact integers beyond binary64
precision MUST encode them under an application-defined
representation, such as JSON strings, rather than rely on this merge
operation to preserve their decimal-token distinction.
This document's acceptance and normalization of negative zero are
explicit. They do not impose the stricter negative-zero parser
behavior discussed by verified Erratum 7920 for [RFC8785], because
JCS conformance is not a merge input requirement here.
4.3. Merge Value Kinds
The merge domain contains these normalized JSON value kinds:
* null;
* boolean;
* number, represented by the binary64 number model (Section 4.2);
* string; and
* object whose values are recursively in this domain.
Arrays are recursively unsupported. An array at the root or below an
object member prevents that complete input from entering the merge
engine.
4.4. Normalized Equality and Kind
Two normalized values are equal when:
* both are null;
* both are the same boolean;
* both are the same normalized binary64 value;
* both are strings containing the same sequence of Unicode scalar
values; or
* both are objects with the same decoded member-name set and
recursively equal values for every member.
Jurkovikj Expires 5 February 2027 [Page 8]
Internet-Draft JSON Three-Way Merge August 2026
Object member order in input JSON does not affect equality. No
Unicode normalization, case folding, locale comparison, or
application-specific coercion is performed.
The five merge value kinds above are distinct. In particular, a
number is not equal to a string containing its decimal spelling, and
null is not equal to a missing object member.
5. Validation and Failure Precedence
5.1. Input-Side Order
The merge processor MUST process complete inputs in this order:
1. base;
2. source; and
3. target.
It MUST stop at the first side that produces an invalid-input,
unsupported-domain, or resource-limit result. The semantic merge
engine runs only after all three inputs are valid, normalized,
recursively array-free, and admitted by the active resource policy.
For example, an invalid base wins over an unsupported array in
source, and an unsupported array in source wins over an invalid
target.
5.2. Within One Input Side
Within one side, processing is bounded and left-to-right. The
processor MUST use this precedence at each parser event:
1. Check the raw input-octet ceiling before scanning.
2. Before entering a recognized container, check nesting depth and
node count.
3. Before constructing a recognized scalar, check node count.
4. When an object member begins, check member count before decoding
and retaining its name; check decoded name size while decoding
it.
5. Detect a duplicate decoded member name before parsing that
member's value.
Jurkovikj Expires 5 February 2027 [Page 9]
Internet-Draft JSON Three-Way Merge August 2026
6. Before parsing an array child, check any per-array element
ceiling and the document node ceiling.
7. Check decoded string size and raw number-token size while
scanning.
8. Report syntax, Unicode, and numeric-domain failures at the first
point they become knowable.
9. If the active policy defines a complete normalized-input byte
ceiling, compute it using the deterministic encoding named by
that policy and check it before admitting the side.
If malformed syntax becomes knowable before a resource boundary is
crossed, status invalid_input wins. If a bounded token reaches the
first prohibited octet before a defect in an unread suffix becomes
knowable, status resource_limit wins. Unsupported-array selection
occurs only after the side is otherwise completely valid and
admitted.
5.3. Unsupported Arrays
For one side, the processor first completes bounded JSON and number
validation. If the valid side contains one or more arrays, it MUST
return status unsupported with:
* code arrays_not_supported;
* side base, source, or target; and
* the RFC 6901 path of the least array under the path ordering
(Section 7.3).
The root array path is the empty string. Equal, unchanged, or empty
arrays are still unsupported. A processor MUST NOT apply an equality
shortcut before array-domain validation.
The selected path is the least path under that ordering, not
necessarily the first array encountered in the raw JSON text. A
processor MAY maintain a running minimum during validation or compare
retained candidate paths after validation; it MUST NOT substitute raw
encounter order for the required path ordering.
Jurkovikj Expires 5 February 2027 [Page 10]
Internet-Draft JSON Three-Way Merge August 2026
The least selected array cannot be reached through an array element.
If an array had an ancestor array, the ancestor's pointer-token
sequence would be a strict prefix and would sort first.
Consequently, the selected array is either the root or is reached
only through object-member steps. Numeric-looking object member
names remain ordinary pointer tokens and are compared by the same
token rules.
An implementation MAY validate unsupported array content without
retaining the complete array, but it MUST still detect malformed
content, duplicate object names, invalid numbers, and resource
failures according to the rules above.
5.4. Invalid Input
An invalid-input result uses one of these codes:
* invalid_json for malformed JSON, invalid UTF-8, trailing content,
or an invalid Unicode scalar sequence;
* duplicate_object_name for a repeated decoded name in one object;
or
* number_out_of_binary64_range for a number that cannot be
represented as a finite binary64 value.
The result MUST identify the failing input side. It MUST contain an
RFC 6901 path when the affected value or containing object is known.
For duplicate_object_name, the path MUST identify the containing
object; the duplicated name is not appended as a child token. For
number_out_of_binary64_range, the path MUST identify the offending
number value itself. A processor MAY include a zero-based UTF-8 byte
offset and derived line and column information. Such optional
source-location details do not alter the merge outcome.
5.5. Resource Policy
Every merge execution MUST be subject to a finite resource policy.
The policy MUST bound, at minimum:
* raw octets per input side;
* container nesting depth;
* total value nodes and object-member occurrences per input;
* array elements processed while validating an unsupported array;
Jurkovikj Expires 5 February 2027 [Page 11]
Internet-Draft JSON Three-Way Merge August 2026
* decoded member-name and string-value octets;
* number-token octets;
* retained conflicts; and
* any serialized result retained by the processor.
A policy MAY additionally bound the bytes in each complete normalized
input. Such a bound is distinct from raw-input, token, node, member,
and serialized result limits. The policy MUST identify the
deterministic normalized-value encoding used for that measurement and
the corresponding limit code. This document neither selects nor
requires a JSON canonicalization scheme for that purpose.
A processor MUST associate its fixed ceilings with a stable resource-
policy identifier. If any ceiling changes, the identifier MUST also
change. Limits are implementation policy rather than universal
ceilings defined by this document, but they are an explicit input to
reproducible operation behavior. An invocation MUST NOT silently
select an unbounded path or raise a ceiling based on attacker-
controlled input.
A resource-limit result MUST identify the resource-policy identifier,
limit code, charged side, path, inclusive limit, and observed value.
To permit early failure without scanning or retaining a hostile
suffix, observed SHOULD be reported as the first prohibited value,
limit + 1.
A processor can define additional limit codes. The following code
spellings SHOULD be used when the corresponding resource is bounded:
input_bytes nesting_depth
total_nodes total_object_members
array_length key_bytes
string_bytes number_token_bytes
normalized_document_bytes result_bytes
merge_response_bytes
conflict_count
When a policy uses one of those suggested codes, the resource-limit
path MUST be selected as follows:
Jurkovikj Expires 5 February 2027 [Page 12]
Internet-Draft JSON Three-Way Merge August 2026
+===========================+====================================+
| Code | Failure path |
+===========================+====================================+
| input_bytes | Root, because the check occurs |
| | before input scanning. |
+---------------------------+------------------------------------+
| nesting_depth | The would-be container path. |
+---------------------------+------------------------------------+
| total_nodes | The would-be value path. |
+---------------------------+------------------------------------+
| total_object_members | The containing object path. |
+---------------------------+------------------------------------+
| array_length | The would-be element path, |
| | including its zero-based array |
| | index. |
+---------------------------+------------------------------------+
| key_bytes | The containing object path; the |
| | oversized name is not admitted. |
+---------------------------+------------------------------------+
| string_bytes | The string value path. |
+---------------------------+------------------------------------+
| number_token_bytes | The number value path. |
+---------------------------+------------------------------------+
| normalized_document_bytes | The first value or member, in the |
| | policy's deterministic normalized- |
| | value encoding order, whose byte |
| | contribution crosses the ceiling; |
| | container punctuation uses the |
| | containing-container path. |
+---------------------------+------------------------------------+
| result_bytes | The first value or member, in |
| | deterministic merge-result |
| | encoding order, whose byte |
| | contribution crosses the ceiling; |
| | container punctuation uses the |
| | containing-container path. |
+---------------------------+------------------------------------+
| merge_response_bytes | The first unadmitted conflict |
| | path; root if a fixed envelope or |
| | a non-conflict response component |
| | crosses the ceiling. |
+---------------------------+------------------------------------+
| conflict_count | The first unadmitted conflict |
| | path. |
+---------------------------+------------------------------------+
Table 1
Jurkovikj Expires 5 February 2027 [Page 13]
Internet-Draft JSON Three-Way Merge August 2026
The path-attribution rule for an additional limit code MUST be
defined by the resource policy. Encoding-derived measurements MUST
attribute each measured byte contribution in a deterministic order.
Path-attribution rules are part of the resource policy; changing one
requires a new resource-policy identifier.
result_bytes measures the deterministic policy-selected serialization
of a complete normalized merged value, excluding the generic result
envelope. It applies only after conflict discovery has established
that the operation is conflict-free. merge_response_bytes measures
the deterministic policy-selected serialization of the complete
generic result form or of a fixed adopting envelope, including
status-specific conflicts or error detail. A policy that uses either
code MUST identify the exact encoding being measured and, for
merge_response_bytes, the exact envelope and included members. If
both codes apply to one successful operation, result_bytes MUST be
checked before merge_response_bytes. Output-side failures use side
merge_result.
Depth counts open object and array containers: a scalar root has
depth zero, a root container has depth one, and entering a child
container increments depth. Every JSON value, including the root and
every unsupported array container, counts as one node. Each
syntactically started object-member occurrence counts once, including
a later-rejected duplicate. Name and string ceilings count decoded
UTF-8 octets; number-token ceilings count the raw token from an
optional minus sign through the final digit of the token, excluding
whitespace.
Resource limits MUST be checked before the allocation, insertion,
recursive descent, clone, or output append that would exceed the
active ceiling. Integer accounting MUST detect overflow rather than
wrap.
A processor claiming conformance to this document MUST configure
limits high enough to execute every applicable conformance vector
(Appendix B).
6. Deterministic Merge Operation
6.1. Equality Shortcuts and Merge Laws
After all three inputs are admitted, each value merge applies these
normalized equality shortcuts in order:
if S == T: return S
if B == S: return T
if B == T: return S
Jurkovikj Expires 5 February 2027 [Page 14]
Internet-Draft JSON Three-Way Merge August 2026
Consequently, over the recursively array-free domain, the operation
satisfies:
M(B, B, B) = B
M(B, S, B) = S
M(B, B, T) = T
M(B, X, X) = X
Swapping source and target preserves a successful merged value and
every conflict path. The two oriented edit-versus-delete conflict
kinds swap as described in Conflict Kinds (Section 7.1).
6.2. Scalar and Type Merge
If no equality shortcut applies and all three values are objects, the
object operation in Object Merge (Section 6.3) applies.
Otherwise, the processor MUST emit one conflict at the current path:
* concurrent_value_change when all three values have the same merge
value kind and they are not all objects; or
* concurrent_type_change when their merge value kinds are not all
the same.
The processor MUST NOT choose source or target based on role name,
textual ordering, timestamp, input member order, or serialization
length.
6.3. Object Merge
If all three values at a path are objects and no equality shortcut
applies, the processor considers the union of their decoded member
names. It processes those names in the unsigned UTF-16 order defined
in Path and Conflict Ordering (Section 7.3) and applies the presence
matrix (Section 6.4) to each member.
When all three member states are present, the processor recursively
applies the value merge at the child path. Independent changes under
different member names therefore combine into one object.
The processor constructs a complete merged object only when no
conflict exists anywhere in the operation. If any conflict exists,
non-conflicting provisional work is discarded and the result contains
only the complete ordered conflict set.
Jurkovikj Expires 5 February 2027 [Page 15]
Internet-Draft JSON Three-Way Merge August 2026
Conflict discovery and conflict-diagnostic limit checks MUST precede
construction or certification of a merged result. A processor MUST
NOT return a merged-result node, member, depth, encoding, or
serialized-size limit while a semantic conflict remains to be
discovered. Those merged-result limits apply only after the
operation is known to be conflict-free. Conflict-count and conflict-
response limits can still produce a complete resource_limit result
during bounded conflict discovery.
6.4. Object-Member Presence Matrix
Presence equality compares both state and, for present states,
normalized value. The following matrix summarizes complete object-
member behavior, including outcomes selected by the same three
equality shortcuts:
In the table, present B in any column means present with a normalized
value equal to the base value denoted by B; it does not require
identical input spelling. S, T, and X likewise denote normalized
values, and != denotes normalized inequality.
+===========+===========+===========+===========================+
| Base | Source | Target | Outcome |
+===========+===========+===========+===========================+
| present B | missing | present B | missing |
+-----------+-----------+-----------+---------------------------+
| present B | present B | missing | missing |
+-----------+-----------+-----------+---------------------------+
| present B | missing | present | source_delete_target_edit |
| | | T, T != B | conflict |
+-----------+-----------+-----------+---------------------------+
| present B | present | missing | source_edit_target_delete |
| | S, S != B | | conflict |
+-----------+-----------+-----------+---------------------------+
| present B | missing | missing | missing |
+-----------+-----------+-----------+---------------------------+
| missing | present S | missing | present S |
+-----------+-----------+-----------+---------------------------+
| missing | missing | present T | present T |
+-----------+-----------+-----------+---------------------------+
| missing | present X | present X | present X |
+-----------+-----------+-----------+---------------------------+
| missing | present S | present | add_add conflict |
| | | T, S != T | |
+-----------+-----------+-----------+---------------------------+
Table 2
Jurkovikj Expires 5 February 2027 [Page 16]
Internet-Draft JSON Three-Way Merge August 2026
If all three states are present, the merge operation (Section 6)
recursively merges their values. If base is missing and source and
target add different objects under the same member name, the
processor reports add_add at that member path. It MUST NOT invent an
empty base object and combine the divergent additions recursively.
6.5. No Partial Result
Every invocation produces exactly one complete status. A conflict
result MUST NOT contain a merged value. A merged result MUST NOT
contain conflicts. An invalid-input, unsupported, or resource-limit
result MUST NOT contain a merged value, a conflict prefix, parser
output, or partially normalized value.
If conflict accumulation or conflict-response measurement reaches a
resource ceiling during conflict discovery, the processor MUST
discard the partial collection and return one complete resource-limit
result. If the operation is conflict-free and merged-result
construction or certification reaches a resource ceiling, it MUST
discard the provisional result and return one complete resource-limit
result.
7. Conflicts and Diagnostic Ordering
7.1. Conflict Kinds
The conflict kinds are:
*concurrent_value_change* Base, source, and target are present with
the same merge value kind, they are not all objects, and both
sides differ from base and from each other.
*concurrent_type_change* Base, source, and target are present; no
equality shortcut applies; and the three merge value kinds are not
all the same.
*add_add* The member is missing from base and both sides add unequal
normalized values.
*source_delete_target_edit* The member is present in base, source
deletes it, and target changes it to a value unequal to base.
*source_edit_target_delete* The member is present in base, source
changes it to a value unequal to base, and target deletes it.
Every conflict contains one kind, one RFC 6901 path, and explicit
base, source, and target presence states. It MUST contain the
complete normalized value for each present state unless an adopting
Jurkovikj Expires 5 February 2027 [Page 17]
Internet-Draft JSON Three-Way Merge August 2026
application defines a separate, non-conforming redacted diagnostic
view. A redacted view cannot be used as the input to automatic
conflict resolution without application-specific rules.
7.2. Presence Encoding
In the JSON result form, presence is encoded as:
{"state":"missing"}
or:
{"state":"present","value":<normalized-json-value>}
The value member MUST be absent when state is missing and MUST be
present when state is present. A present value can be JSON null.
7.3. Path and Conflict Ordering
Paths are RFC 6901 JSON Pointers. The document root is the empty
string. For a member name, ~ is escaped as ~0 and / is escaped as
~1.
Ordering compares decoded pointer-token sequences, not rendered
pointer strings. Each token is compared lexicographically as a
sequence of unsigned UTF-16 code units. If all units in the shorter
token are equal to the prefix of the longer token, the shorter token
sorts first. If all shared tokens are equal, the pointer with fewer
tokens sorts first.
This comparison is locale-independent and does not apply Unicode
normalization. A supplementary Unicode scalar value is compared
through its UTF-16 surrogate pair. Lone surrogates cannot occur
because they are rejected during input processing.
UTF-16 code-unit order deliberately differs from Unicode scalar-value
order for some comparisons between supplementary and Basic
Multilingual Plane characters. An implementation whose native string
ordering uses scalar values or UTF-8 bytes therefore MUST explicitly
derive the UTF-16 code-unit order rather than use its runtime's
default string comparison.
The operation defined by this document produces at most one conflict
for one logical path. The conflict array MUST be sorted by the path
ordering above. Object-member traversal and least-array selection
MUST use the same ordering.
Jurkovikj Expires 5 February 2027 [Page 18]
Internet-Draft JSON Three-Way Merge August 2026
8. JSON Result Form
This section defines a generic JSON representation of an operation
result. It does not register a media type or require an application
to transfer results over a network. An adopting specification MAY
wrap these members in a versioned envelope, but MUST preserve their
meanings. The ordering requirement applies to entries in the
conflicts array, not to JSON object members.
JSON object member order and insignificant whitespace in this result
form are not significant. Every emitted number value MUST represent
the corresponding normalized binary64 value when parsed under the
binary64 number model (Section 4.2), and negative zero MUST NOT be
emitted. When serialized, this result form MUST use UTF-8 and MUST
NOT begin with a byte order mark.
Every generic result MUST contain the string member status and
exactly the status-specific top-level member shown below. The other
two status-specific members in the table MUST be absent. Additional
envelope or error-detail members remain permitted as described below.
+================+===========================+================+
| status | Required top-level member | Forbidden top- |
| | | level members |
+================+===========================+================+
| merged | merged | conflicts, |
| | | error |
+----------------+---------------------------+----------------+
| conflict | conflicts | merged, error |
+----------------+---------------------------+----------------+
| unsupported | error | merged, |
| | | conflicts |
+----------------+---------------------------+----------------+
| invalid_input | error | merged, |
| | | conflicts |
+----------------+---------------------------+----------------+
| resource_limit | error | merged, |
| | | conflicts |
+----------------+---------------------------+----------------+
Table 3
8.1. Merged
{
"status": "merged",
"merged": {"name":"ready","owner":"B"}
}
Jurkovikj Expires 5 February 2027 [Page 19]
Internet-Draft JSON Three-Way Merge August 2026
The merged member MUST contain the complete normalized merged value.
8.2. Conflict
{
"status": "conflict",
"conflicts": [
{
"kind": "concurrent_value_change",
"path": "/title",
"base": {"state":"present","value":"Draft"},
"source": {"state":"present","value":"Ready"},
"target": {"state":"present","value":"Review"}
}
]
}
The conflicts array MUST be non-empty and ordered as specified in
Path and Conflict Ordering (Section 7.3).
8.3. Unsupported
{
"status": "unsupported",
"error": {
"code": "arrays_not_supported",
"side": "source",
"path": "/items"
}
}
8.4. Invalid Input
{
"status": "invalid_input",
"error": {
"code": "duplicate_object_name",
"side": "base",
"path": ""
}
}
8.5. Resource Limit
Jurkovikj Expires 5 February 2027 [Page 20]
Internet-Draft JSON Three-Way Merge August 2026
{
"status": "resource_limit",
"error": {
"budget": "example-resource-budget-v1",
"code": "nesting_depth",
"side": "target",
"path": "/configuration",
"limit": 64,
"observed": 65
}
}
The required error members are:
+================+========================+=================+
| status | Required error members | Additional |
| | | defined members |
+================+========================+=================+
| unsupported | code, side, path | none |
+----------------+------------------------+-----------------+
| invalid_input | code, side; path when | byte_offset, |
| | known | line, column |
+----------------+------------------------+-----------------+
| resource_limit | budget, code, side, | none |
| | path, limit, observed | |
+----------------+------------------------+-----------------+
Table 4
For unsupported and invalid_input, side is base, source, or target.
For resource_limit, those values identify an input-side charge and
merge_result identifies conflict or result construction. In a
resource_limit error, budget is a string containing the
resource-policy identifier (Section 5.5). Both limit and observed
are non-negative integers. Status-specific codes and path rules are
defined in Validation and Failure Precedence (Section 5).
An implementation can add envelope or error-detail members, but MUST
NOT use a defined status, conflict kind, or error code with a
different meaning.
9. Examples
9.1. Independent Object Changes
Given:
Jurkovikj Expires 5 February 2027 [Page 21]
Internet-Draft JSON Three-Way Merge August 2026
base = {"name":"draft","owner":"A"}
source = {"name":"ready","owner":"A"}
target = {"name":"draft","owner":"B"}
source changes name, target changes owner, and neither change
overlaps. The merged value is:
{"name":"ready","owner":"B"}
9.2. Missing Is Not Null
Given an empty base, source adds a present null member and target
remains unchanged:
base = {}
source = {"reviewed":null}
target = {}
The merged value is {"reviewed":null}. The member is not deleted
because a present JSON null is a value, not the missing state.
9.3. Delete Versus Edit
Given:
base = {"title":"Draft"}
source = {}
target = {"title":"Ready"}
the result contains source_delete_target_edit at /title, with source
encoded as missing and the other two states encoded as present.
9.4. Divergent Additions Are Conservative
Given:
base = {}
source = {"settings":{"left":true}}
target = {"settings":{"right":true}}
the result is add_add at /settings. Although the two added objects
contain different member names, they have no shared base object.
This operation does not invent one and silently combine both
additions.
Jurkovikj Expires 5 February 2027 [Page 22]
Internet-Draft JSON Three-Way Merge August 2026
9.5. Binary64 Equality
Given:
base = {"count":1}
source = {"count":1.0}
target = {"count":2}
base and source are equal after number normalization, so the merged
value is {"count":2}.
10. Informative HTTP Reconciliation Example
This merge operation does not depend on HTTP. An HTTP application
can use it after a conditional state-changing request fails because
the resource changed [RFC9110]:
1. base is the representation state previously observed by the
client;
2. source is the client's complete proposed state derived from base;
3. target is freshly obtained current state;
4. the application runs this merge operation;
5. conflicts are reviewed or resolved under application policy; and
6. a successful candidate is validated and submitted using a
precondition for the newly observed current state.
A successful structural merge does not authorize a blind retry. The
application still needs current-state validation, authorization,
schema and policy checks, and an atomic commit mechanism. Agentic
State Transfer [I-D.jurkovikj-httpapi-agentic-state] is an example of
an HTTP profile whose application layer can perform reconciliation
after a precondition failure; it does not normatively depend on this
document.
11. Security Considerations
11.1. Resource Exhaustion
JSON depth, width, token size, and conflict count are attacker-
controlled. Processors MUST enforce the resource policy
(Section 5.5) before prohibited recursion or allocation. Rejecting
arrays does not permit unbounded parsing merely to discover that an
array is unsupported.
Jurkovikj Expires 5 February 2027 [Page 23]
Internet-Draft JSON Three-Way Merge August 2026
11.2. Conflict-Value Disclosure
A conforming conflict contains values from base, source, and target.
Those values can include credentials, personal information, deleted
content, configuration secrets, or fields hidden from one
participant. Conflict results MUST be treated as sensitive
application data. Authentication and authorization checks MUST
precede disclosure. Implementations MUST NOT log, render, or
transmit raw conflicts by default when doing so would expose data to
an unauthorized party. An authorized interface that presents
conflict values MUST encode them as untrusted structured data for the
destination context and MUST NOT interpret them as HTML, terminal
controls, executable content, or format strings.
An application can create a redacted human-facing view, but that view
is not a conforming complete conflict and might omit information
required for safe resolution.
11.3. Merge Success Is Not Semantic Safety
This operation detects structural overlap under its fixed rules. It
does not know application invariants. Independent edits can merge
structurally while violating a schema, cross-field constraint,
business rule, authorization condition, or safety policy.
Applications MUST validate the complete candidate under current
policy before commit.
Automated agents MUST NOT interpret status merged as approval or
authority to perform an external action.
11.4. Base Authenticity and Current State
An incorrect or attacker-selected base can make concurrent changes
appear one-sided. Applications need an authenticated way to
associate base with the states actually observed by both editors. In
concurrent systems, current-state validation and candidate commit
need to be atomic or protected by an appropriate compare-and-swap
mechanism.
11.5. Binary64 Rounding
Decimal tokens can collapse to one binary64 value, including large
integers outside the exactly representable range. Applications MUST
NOT use this merge number model for identifiers, account balances,
counters, or other values that require decimal or arbitrary-precision
distinction unless their application representation encodes that
distinction explicitly.
Jurkovikj Expires 5 February 2027 [Page 24]
Internet-Draft JSON Three-Way Merge August 2026
11.6. Unicode and Path Presentation
No Unicode normalization is performed. Visually similar member names
can remain distinct. Interfaces SHOULD display escaped paths and
untrusted member names in a form that prevents control-character
injection, bidirectional-text confusion, and HTML or terminal
interpretation. Processors MUST compare decoded logical pointer
tokens, not presentation strings.
11.7. Unsupported Arrays
Treating an unsupported result as permission to fall back to last-
writer-wins, index-wise merge, or an older unversioned array
algorithm can silently discard data. Applications SHOULD preserve
the last known good state and require a separately identified array-
aware profile or explicit review.
12. IANA Considerations
This document has no IANA actions.
13. Implementation Status
This section follows the guidance in [RFC7942]. It records
implementation information as of 2026-08-04 and is expected to be
removed before publication as an RFC.
libdualnative provides a Rust implementation of the scalar/object
operation, typed conflicts, array rejection, and fixed resource
failures specified here. Its policy-specific boundary suite
exercises exact and first-prohibited ceilings, failure precedence,
conflict atomicity, and no-partial-result behavior. Its WebAssembly
package is a facade over the same Rust engine and is parity evidence,
not an independent implementation. The experimental v0.1.0-alpha.1
GitHub prerelease is available at https://github.com/DualNative/
libdualnative-v1/releases/tag/v0.1.0-alpha.1
(https://github.com/DualNative/libdualnative-v1/releases/tag/
v0.1.0-alpha.1). The release source commit is
d986f1ab9b851dea8dd8d4a99dac6ef92acd8f25.
The implementation also exposes a separate RFC 8785 and SHA-256
content identity operation. That operation is not required by this
document and is not evidence of an independent merge implementation.
At the time of writing, no complete, independently maintained
implementation in another language is known. The machine-readable
companion suite contains 41 vectors and has SHA-256 digest
13467e8bf70718bdf247da4b007730e4f0e560f538db6a86f92a88a457c35e62.
Jurkovikj Expires 5 February 2027 [Page 25]
Internet-Draft JSON Three-Way Merge August 2026
During draft preparation, the Rust reference implementation was run
against all 41 companion vectors. A temporary JavaScript cross-check
independently exercised the 33 successful-merge, conflict, and
unsupported-array vectors. It did not implement strict input-error
or resource-limit processing and is not a complete or maintained
implementation. A complete independent implementation and common-
vector execution are desirable before seeking working-group adoption.
14. References
14.1. Normative References
[IEEE754] IEEE, "IEEE Standard for Floating-Point Arithmetic",
IEEE 754-2019, DOI 10.1109/IEEESTD.2019.8766229, 18 July
2019, <https://ieeexplore.ieee.org/document/8766229>.
[RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed.,
"JavaScript Object Notation (JSON) Pointer", RFC 6901,
DOI 10.17487/RFC6901, April 2013,
<https://www.rfc-editor.org/rfc/rfc6901>.
[RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
Interchange Format", STD 90, RFC 8259,
DOI 10.17487/RFC8259, December 2017,
<https://www.rfc-editor.org/rfc/rfc8259>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/rfc/rfc2119>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/rfc/rfc8174>.
14.2. Informative References
[RFC6902] Bryan, P., Ed. and M. Nottingham, Ed., "JavaScript Object
Notation (JSON) Patch", RFC 6902, DOI 10.17487/RFC6902,
April 2013, <https://www.rfc-editor.org/rfc/rfc6902>.
[RFC7396] Hoffman, P. and J. Snell, "JSON Merge Patch", RFC 7396,
DOI 10.17487/RFC7396, October 2014,
<https://www.rfc-editor.org/rfc/rfc7396>.
[RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493,
DOI 10.17487/RFC7493, March 2015,
<https://www.rfc-editor.org/rfc/rfc7493>.
Jurkovikj Expires 5 February 2027 [Page 26]
Internet-Draft JSON Three-Way Merge August 2026
[RFC8785] Rundgren, A., Jordan, B., and S. Erdtman, "JSON
Canonicalization Scheme (JCS)", RFC 8785,
DOI 10.17487/RFC8785, June 2020,
<https://www.rfc-editor.org/rfc/rfc8785>.
[RFC9110] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
Ed., "HTTP Semantics", STD 97, RFC 9110,
DOI 10.17487/RFC9110, June 2022,
<https://www.rfc-editor.org/rfc/rfc9110>.
[I-D.jurkovikj-httpapi-agentic-state]
Jurkovikj, A., "HTTP Profile for Conditional Updates to
Shared Resource State (Agentic State Transfer)", Work in
Progress, Internet-Draft, draft-jurkovikj-httpapi-agentic-
state-02, 27 July 2026,
<https://datatracker.ietf.org/doc/html/draft-jurkovikj-
httpapi-agentic-state-02>.
[RFC7942] Sheffer, Y. and A. Farrel, "Improving Awareness of Running
Code: The Implementation Status Section", BCP 205,
RFC 7942, DOI 10.17487/RFC7942, July 2016,
<https://www.rfc-editor.org/rfc/rfc7942>.
Appendix A. Merge Pseudocode
The following pseudocode is explanatory. Normative behavior is
defined in the preceding sections.
MERGE-VALUE(B, S, T, path):
if EQUAL(S, T): return VALUE(S)
if EQUAL(B, S): return VALUE(T)
if EQUAL(B, T): return VALUE(S)
if OBJECT(B) and OBJECT(S) and OBJECT(T):
return MERGE-OBJECT(B, S, T, path)
if KIND(B) == KIND(S) and KIND(S) == KIND(T):
return CONFLICT(concurrent_value_change, path,
PRESENT(B), PRESENT(S), PRESENT(T))
return CONFLICT(concurrent_type_change, path,
PRESENT(B), PRESENT(S), PRESENT(T))
MERGE-MEMBER(b, s, t, path):
if EQUAL-PRESENCE(s, t): return s
if EQUAL-PRESENCE(b, s): return t
if EQUAL-PRESENCE(b, t): return s
Jurkovikj Expires 5 February 2027 [Page 27]
Internet-Draft JSON Three-Way Merge August 2026
match (b, s, t):
(PRESENT(B), PRESENT(S), PRESENT(T)):
return MERGE-VALUE(B, S, T, path)
(MISSING, PRESENT(S), PRESENT(T)):
return CONFLICT(add_add, path, MISSING,
PRESENT(S), PRESENT(T))
(PRESENT(B), MISSING, PRESENT(T)):
return CONFLICT(source_delete_target_edit, path,
PRESENT(B), MISSING, PRESENT(T))
(PRESENT(B), PRESENT(S), MISSING):
return CONFLICT(source_edit_target_delete, path,
PRESENT(B), PRESENT(S), MISSING)
MERGE-OBJECT(B, S, T, path):
provisional = empty object
conflicts = empty sequence
for key in SORT-UTF16(UNION-OF-NAMES(B, S, T)):
child = MERGE-MEMBER(PRESENCE(B, key),
PRESENCE(S, key),
PRESENCE(T, key),
APPEND-POINTER(path, key))
if child is conflict collection:
append child conflicts
else if child is present:
provisional[key] = child.value
if conflicts is not empty:
discard provisional
return SORT-BY-POINTER(conflicts)
return provisional
Appendix B. Conformance Vectors
The inputs in this appendix are JSON texts. Expected merged values
and conflict presence values are compared after normalization, so
insignificant whitespace, object member order, and equivalent
binary64 number spelling do not alter a vector result.
Jurkovikj Expires 5 February 2027 [Page 28]
Internet-Draft JSON Three-Way Merge August 2026
A machine-readable companion suite maintained with the draft source
is named draft-jurkovikj-json-three-way-merge-00-vectors.json and
uses schema identifier draft-jurkovikj-json-three-way-merge-00-
vectors-v1. Its base, source, and target members are outer JSON
strings whose decoded contents are the exact UTF-8 input text
supplied to the processor. This permits malformed JSON text, a
leading byte order mark, and an escaped lone surrogate to remain
expressible inside a valid suite file. A truly invalid UTF-8 octet
sequence requires a separate byte-oriented suite. Each expected
object uses the generic result-form names defined by this document;
comparisons of merged and conflict values occur after normalization.
Concrete resource ceilings are implementation policy, so this
appendix does not assign one universal N/N+1 resource-limit vector.
Policy-specific suites can add boundary vectors bound to their
resource-policy identifier and exact ceilings.
B.1. Successful Merge Vectors
* no-change: base {"x":1}; source {"x":1.0}; target {"x":1e0};
merged {"x":1}.
* source-only: base {"x":1}; source {"x":2}; target {"x":1}; merged
{"x":2}.
* target-only: base {"x":1}; source {"x":1}; target {"x":2}; merged
{"x":2}.
* agreement: base {"x":1}; source {"x":2}; target {"x":2.0}; merged
{"x":2}.
* independent-members: base {"a":0,"b":0}; source {"a":1,"b":0};
target {"a":0,"b":2}; merged {"a":1,"b":2}.
* source-delete: base {"x":1}; source {}; target {"x":1}; merged {}.
* target-delete: base {"x":1}; source {"x":1}; target {}; merged {}.
* both-delete: base {"x":1}; source {}; target {}; merged {}.
* one-sided-add-null: base {}; source {"x":null}; target {}; merged
{"x":null}.
* target-only-add: base {}; source {}; target {"x":1}; merged
{"x":1}.
* agreed-add: base {}; source {"x":1}; target {"x":1.0}; merged
{"x":1}.
Jurkovikj Expires 5 February 2027 [Page 29]
Internet-Draft JSON Three-Way Merge August 2026
* agreed-type-change: base 1; source {"x":2}; target {"x":2}; merged
{"x":2}.
* one-sided-type-change: base 1; source 1.0; target {"x":2}; merged
{"x":2}.
* large-integer-normalization: base 9007199254740992; source
9007199254740993; target 3; merged 3.
* negative-zero-normalization: base {"x":-0}; source {"x":0.0};
target {"x":-0.0}; merged {"x":0}. A serialized merged result MUST
NOT contain negative zero.
* subnormal-equality: base 5e-324; source 4.9e-324; target 1; merged
1 because the first two tokens convert to the same binary64 value.
B.2. Conflict Vectors
Each expected conflict below is the sole conflict unless an ordered
path list is shown.
* scalar-divergence: base 1; source 2; target 3; kind
concurrent_value_change; path "".
* type-divergence: base 1; source "source"; target {"x":2}; kind
concurrent_type_change; path "".
* add-add: base {}; source {"x":1}; target {"x":2}; kind add_add;
path /x.
* delete-edit: base {"x":1}; source {}; target {"x":2}; kind
source_delete_target_edit; path /x.
* edit-delete: base {"x":1}; source {"x":2}; target {}; kind
source_edit_target_delete; path /x.
* divergent-added-objects: base {}; source {"x":{"a":1}}; target
{"x":{"b":2}}; kind add_add; path /x.
* null-versus-delete: base {"x":1}; source {"x":null}; target {};
kind source_edit_target_delete; path /x.
* add-add-null: base {}; source {"x":null}; target {"x":1}; kind
add_add; path /x.
* nested-type-conflict: base {"a":{"b":1}}; source
{"a":{"b":"source"}}; target {"a":{"b":true}}; kind
concurrent_type_change; path /a/b.
Jurkovikj Expires 5 February 2027 [Page 30]
Internet-Draft JSON Three-Way Merge August 2026
The decoded-token-sequence-order vector distinguishes decoded token-
sequence ordering from rendered-pointer-string ordering:
{
"base": {"a":{"b":0},"a!b":0},
"source": {"a":{"b":1},"a!b":1},
"target": {"a":{"b":2},"a!b":2},
"expected_status": "conflict",
"expected_paths": ["/a/b","/a!b"]
}
The first decoded token a is a prefix of a!b, so /a/b sorts first.
Comparing the rendered pointer strings would incorrectly put /a!b
first because ! precedes /.
The empty-member-name-order vector uses base {"":0,"a":0}, source
{"":1,"a":1}, and target {"":2,"a":2}. Its expected conflict paths
are ["/","/a"]; the first pointer contains one empty decoded token.
The escaped-path-order vector checks escaped paths and deterministic
ordering:
{
"base": {"z":0,"a/b":0,"m~n":0},
"source": {"z":1,"a/b":1,"m~n":1},
"target": {"z":2,"a/b":2,"m~n":2},
"expected_status": "conflict",
"expected_paths": ["/a~1b","/m~0n","/z"]
}
All three conflicts have kind concurrent_value_change.
The utf16-path-order vector makes the UTF-16 ordering rule visible
when a supplementary character and a Basic Multilingual Plane
character are compared:
{
"base": {"\ue000":0,"\ud83d\ude00":0},
"source": {"\ue000":1,"\ud83d\ude00":1},
"target": {"\ue000":2,"\ud83d\ude00":2},
"expected_status": "conflict",
"expected_paths": ["/\ud83d\ude00","/\ue000"]
}
The grinning-face character sorts first because its leading UTF-16
code unit is 0xD83D, which is less than 0xE000.
Jurkovikj Expires 5 February 2027 [Page 31]
Internet-Draft JSON Three-Way Merge August 2026
B.3. Unsupported-Array Vectors
* root-array: base []; source {}; target {}; side base; path "".
* nested-array: base {"items":null}; source {"items":[1]}; target
{"items":null}; side source; path /items.
* equal-arrays: all three inputs are {"items":[1]}; side base; path
/items.
* least-array-path: base {"z":[],"a":{"items":[]}}; source {};
target {}; side base; path /a/items.
B.4. Invalid-Input Precedence Vectors
* invalid-base-first: base {broken; source []; target {}; status
invalid_input; side base; code invalid_json; path "".
* source-array-before-target: base {}; source []; target {broken;
status unsupported; side source; code arrays_not_supported.
* decoded-duplicate: base {"a":1,"\u0061":2}; source {}; target {};
status invalid_input; side base; code duplicate_object_name.
* number-overflow: base {}; source 1e309; target {}; status
invalid_input; side source; code number_out_of_binary64_range.
* nested-decoded-duplicate-path: base {"outer":{"a":1,"\u0061":2}};
source {}; target {}; status invalid_input; side base; code
duplicate_object_name; path /outer.
* nested-number-overflow-path: base {}; source
{"outer":{"n":1e309}}; target {}; status invalid_input; side
source; code number_out_of_binary64_range; path /outer/n.
* leading-bom: base is U+FEFF followed by {}; source {}; target {};
status invalid_input; side base; code invalid_json; path "".
* lone-surrogate-escape: base "\uD800"; source {}; target {}; status
invalid_input; side base; code invalid_json; path "".
Appendix C. Design Rationale
Jurkovikj Expires 5 February 2027 [Page 32]
Internet-Draft JSON Three-Way Merge August 2026
C.1. Why Arrays Fail Closed
An array can represent a sequence, set, keyed collection, priority
list, ordered log, tuple, or application-specific structure. A
generic processor cannot infer whether index identity, element
identity, order, or movement is semantically significant. Returning
a typed unsupported result avoids standardizing an unsafe guess and
leaves room for separately identified array profiles.
C.2. Why Divergent Additions Do Not Recurse
When a member is missing from base, there is no shared object at that
path. Recursively merging two divergent added objects against an
invented empty base would turn independent creation into implicit
agreement. Reporting add_add keeps that decision visible to
application policy.
C.3. Why Merge Is Value-Deterministic Rather Than Byte-Canonical
JSON permits insignificant whitespace, multiple number spellings, and
unordered object members. This document fixes normalized values and
diagnostic sequence order, which are the interoperability
requirements for merge. It leaves byte canonicalization and content
identity to specifications designed for those purposes.
C.4. Why UTF-16 Code-Unit Ordering
The comparator for one decoded pointer token aligns with the
property-name comparator defined by JCS. It also maps directly to
string representations in ECMAScript, Java, and .NET. Runtimes that
natively compare Unicode scalar values or UTF-8 bytes need an
explicit UTF-16 conversion or equivalent code-unit iterator. This
comparator alignment permits reuse of tested sorting routines, but
complete paths are ordered as token sequences by this document and
JCS remains unnecessary for merge processing.
Author's Address
Antun Jurkovikj
North Macedonia
Email: antunjurkovic@gmail.com
Jurkovikj Expires 5 February 2027 [Page 33]