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":} 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, . [RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed., "JavaScript Object Notation (JSON) Pointer", RFC 6901, DOI 10.17487/RFC6901, April 2013, . [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, December 2017, . [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, . [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, . 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, . [RFC7396] Hoffman, P. and J. Snell, "JSON Merge Patch", RFC 7396, DOI 10.17487/RFC7396, October 2014, . [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, DOI 10.17487/RFC7493, March 2015, . 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, . [RFC9110] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", STD 97, RFC 9110, DOI 10.17487/RFC9110, June 2022, . [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, . [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, . 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]