{ "description": "Composer serialization contract (issue #890): composer doc shape (the markdown.json tree vocabulary) -> exact wire bytes. The web serializer (composer-md.js serializeDoc) is the authority; the native twin (ComposerSerialize.swift) must emit identical bytes. `reparse` documents the accepted serialize-then-parse non-identities (expelled whitespace, code padding, typed double breaks becoming paragraphs); absent means parseMessage(md) returns the input tree. Scope notes: no nested blocks, no links from typing, no breaks inside list items — unreachable from composer typing on both platforms (web binds neither sinkListItem nor Tab; native has literal edit-load and plain paste). When a twin disagrees, fix the implementation, never the vector.", "vectors": [ { "name": "plain_single", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "hello" } ] } ], "md": "hello" }, { "name": "plain_multiline", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "a" }, { "t": "br" }, { "t": "text", "text": "b" } ] } ], "md": "a\nb", "note": "hard_break is a plain \\n on the wire" }, { "name": "double_break", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "a" }, { "t": "br" }, { "t": "br" }, { "t": "text", "text": "b" } ] } ], "md": "a\n\nb", "reparse": [ { "t": "p", "children": [ { "t": "text", "text": "a" } ] }, { "t": "p", "children": [ { "t": "text", "text": "b" } ] } ], "note": "two typed breaks read back as two paragraphs" }, { "name": "bold", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "b" } ] } ] } ], "md": "**b**" }, { "name": "italic", "tree": [ { "t": "p", "children": [ { "t": "em", "children": [ { "t": "text", "text": "i" } ] } ] } ], "md": "*i*" }, { "name": "strike_single_tilde", "tree": [ { "t": "p", "children": [ { "t": "strike", "children": [ { "t": "text", "text": "s" } ] } ] } ], "md": "~s~", "note": "single tilde — old clients render it perfectly" }, { "name": "inline_code", "tree": [ { "t": "p", "children": [ { "t": "code", "text": "c" } ] } ], "md": "`c`" }, { "name": "bold_italic", "tree": [ { "t": "p", "children": [ { "t": "em", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "bi" } ] } ] } ] } ], "md": "***bi***", "note": "mark rank: em outermost" }, { "name": "strong_strike", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "strike", "children": [ { "t": "text", "text": "x" } ] } ] } ] } ], "md": "**~x~**" }, { "name": "em_strike", "tree": [ { "t": "p", "children": [ { "t": "em", "children": [ { "t": "strike", "children": [ { "t": "text", "text": "x" } ] } ] } ] } ], "md": "*~x~*" }, { "name": "em_strong_strike", "tree": [ { "t": "p", "children": [ { "t": "em", "children": [ { "t": "strong", "children": [ { "t": "strike", "children": [ { "t": "text", "text": "x" } ] } ] } ] } ] } ], "md": "***~x~***" }, { "name": "mixable_continuation", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "bold " } ] }, { "t": "em", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "inner" } ] } ] }, { "t": "strong", "children": [ { "t": "text", "text": " bold" } ] } ] } ], "md": "**bold *inner* bold**", "reparse": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "bold " }, { "t": "em", "children": [ { "t": "text", "text": "inner" } ] }, { "t": "text", "text": " bold" } ] } ] } ], "note": "the already-open strong stays outside" }, { "name": "escape_star_in_strong", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "a*b" } ] } ] } ], "md": "**a\\*b**", "note": "in-mark escapes only" }, { "name": "escape_star_in_em", "tree": [ { "t": "p", "children": [ { "t": "em", "children": [ { "t": "text", "text": "2*3" } ] } ] } ], "md": "*2\\*3*" }, { "name": "escape_tilde_in_strike", "tree": [ { "t": "p", "children": [ { "t": "strike", "children": [ { "t": "text", "text": "a~b" } ] } ] } ], "md": "~a\\~b~" }, { "name": "zero_escape_plain", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "2*3 and ~x~ stay" } ] } ], "md": "2*3 and ~x~ stay", "reparse": [ { "t": "p", "children": [ { "t": "text", "text": "2*3 and " }, { "t": "strike", "children": [ { "t": "text", "text": "x" } ] }, { "t": "text", "text": " stay" } ] } ], "note": "plain text serializes verbatim, never a backslash" }, { "name": "code_tick_sizing", "tree": [ { "t": "p", "children": [ { "t": "code", "text": "a`b" } ] } ], "md": "`` a`b ``", "note": "delimiters sized past inner backticks, space-padded" }, { "name": "code_double_tick", "tree": [ { "t": "p", "children": [ { "t": "code", "text": "a``b" } ] } ], "md": "``` a``b ```" }, { "name": "code_space_padding", "tree": [ { "t": "p", "children": [ { "t": "code", "text": " x " } ] } ], "md": "` x `", "reparse": [ { "t": "p", "children": [ { "t": "code", "text": "x" } ] } ] }, { "name": "code_beside_marks", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "b" } ] }, { "t": "text", "text": " " }, { "t": "code", "text": "c" } ] } ], "md": "**b** `c`" }, { "name": "expel_trailing_ws", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "a " } ] }, { "t": "text", "text": "b" } ] } ], "md": "**a** b", "reparse": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "a" } ] }, { "t": "text", "text": " b" } ] } ], "note": "expelEnclosingWhitespace" }, { "name": "expel_leading_ws", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "a" }, { "t": "strong", "children": [ { "t": "text", "text": " b" } ] } ] } ], "md": "a **b**", "reparse": [ { "t": "p", "children": [ { "t": "text", "text": "a " }, { "t": "strong", "children": [ { "t": "text", "text": "b" } ] } ] } ] }, { "name": "ws_only_styled", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "a" }, { "t": "strong", "children": [ { "t": "text", "text": " " } ] }, { "t": "text", "text": "b" } ] } ], "md": "a b", "reparse": [ { "t": "p", "children": [ { "t": "text", "text": "a b" } ] } ], "note": "whitespace-only styled run collapses to bare whitespace" }, { "name": "marked_break_spans", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "a" }, { "t": "br" }, { "t": "text", "text": "b" } ] } ] } ], "md": "**a\nb**", "note": "select-across-lines ⌘B: the break carries the mark" }, { "name": "unmarked_break_closes", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "a" } ] }, { "t": "br" }, { "t": "strong", "children": [ { "t": "text", "text": "b" } ] } ] } ], "md": "**a**\n**b**", "note": "typed Shift+Enter: plain break closes and reopens the mark" }, { "name": "break_mark_not_continuing", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "a" }, { "t": "br" } ] }, { "t": "text", "text": "b" } ] } ], "md": "**a**\nb", "reparse": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "a" } ] }, { "t": "br" }, { "t": "text", "text": "b" } ] } ], "note": "a break's mark drops when the mark doesn't continue" }, { "name": "unicode_styled", "tree": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "líne ✨ emoji" } ] } ] } ], "md": "**líne ✨ emoji**" }, { "name": "bullet_list", "tree": [ { "t": "ul", "items": [ [ { "t": "p", "children": [ { "t": "text", "text": "a" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "b" } ] } ] ] } ], "md": "- a\n- b" }, { "name": "bullet_styled_item", "tree": [ { "t": "ul", "items": [ [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "b" } ] }, { "t": "text", "text": " x" } ] } ] ] } ], "md": "- **b** x" }, { "name": "ordered_basic", "tree": [ { "t": "ol", "start": 1, "items": [ [ { "t": "p", "children": [ { "t": "text", "text": "a" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "b" } ] } ] ] } ], "md": "1. a\n2. b" }, { "name": "ordered_start3", "tree": [ { "t": "ol", "start": 3, "items": [ [ { "t": "p", "children": [ { "t": "text", "text": "a" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "b" } ] } ] ] } ], "md": "3. a\n4. b" }, { "name": "ordered_width_start9", "tree": [ { "t": "ol", "start": 9, "items": [ [ { "t": "p", "children": [ { "t": "text", "text": "a" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "b" } ] } ] ] } ], "md": " 9. a\n10. b", "note": "width from digits(start+count-1): ' 9. a' aligns under '10. b'" }, { "name": "ordered_ten_items", "tree": [ { "t": "ol", "start": 1, "items": [ [ { "t": "p", "children": [ { "t": "text", "text": "item1" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item2" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item3" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item4" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item5" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item6" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item7" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item8" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item9" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "item10" } ] } ] ] } ], "md": " 1. item1\n 2. item2\n 3. item3\n 4. item4\n 5. item5\n 6. item6\n 7. item7\n 8. item8\n 9. item9\n10. item10" }, { "name": "quote_single", "tree": [ { "t": "quote", "children": [ { "t": "p", "children": [ { "t": "text", "text": "q" } ] } ] } ], "md": "> q" }, { "name": "quote_styled", "tree": [ { "t": "quote", "children": [ { "t": "p", "children": [ { "t": "strong", "children": [ { "t": "text", "text": "b" } ] }, { "t": "text", "text": " i" } ] } ] } ], "md": "> **b** i" }, { "name": "quote_multiline", "tree": [ { "t": "quote", "children": [ { "t": "p", "children": [ { "t": "text", "text": "a" }, { "t": "br" }, { "t": "text", "text": "b" } ] } ] } ], "md": "> a\n> b" }, { "name": "quote_double_break", "tree": [ { "t": "quote", "children": [ { "t": "p", "children": [ { "t": "text", "text": "a" }, { "t": "br" }, { "t": "br" }, { "t": "text", "text": "b" } ] } ] } ], "md": "> a\n> \n> b", "reparse": [ { "t": "quote", "children": [ { "t": "p", "children": [ { "t": "text", "text": "a" } ] }, { "t": "p", "children": [ { "t": "text", "text": "b" } ] } ] } ], "note": "the blank quoted line carries a trailing space" }, { "name": "fence_plain", "tree": [ { "t": "codeblock", "text": "code", "lang": "" } ], "md": "```\ncode\n```" }, { "name": "fence_lang", "tree": [ { "t": "codeblock", "text": "code **here**", "lang": "js" } ], "md": "```js\ncode **here**\n```" }, { "name": "fence_inner_backticks", "tree": [ { "t": "codeblock", "text": "x = ```\ny", "lang": "" } ], "md": "````\nx = ```\ny\n````" }, { "name": "fence_empty", "tree": [ { "t": "codeblock", "text": "", "lang": "" } ], "md": "```\n```" }, { "name": "fence_tildes_stay", "tree": [ { "t": "codeblock", "text": "~~~", "lang": "" } ], "md": "```\n~~~\n```", "note": "tildes never grow a backtick fence" }, { "name": "mixed_blocks", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "intro" } ] }, { "t": "ul", "items": [ [ { "t": "p", "children": [ { "t": "text", "text": "one" } ] } ], [ { "t": "p", "children": [ { "t": "text", "text": "two" } ] } ] ] }, { "t": "quote", "children": [ { "t": "p", "children": [ { "t": "text", "text": "bye" } ] } ] } ], "md": "intro\n\n- one\n- two\n\n> bye" }, { "name": "para_then_list", "tree": [ { "t": "p", "children": [ { "t": "text", "text": "a" }, { "t": "br" }, { "t": "text", "text": "b" } ] }, { "t": "ul", "items": [ [ { "t": "p", "children": [ { "t": "text", "text": "x" } ] } ] ] } ], "md": "a\nb\n\n- x" } ] }