Cases
These are real failures we hit while sending agent-written Markdown to chat channels. Each one is in the test cases with its input and the expected output per channel, and the Rust core, the npm package, the Go implementation and the CLI all produce the same result. The test cases hold more than are shown here.
Emphasis spanning lines
Prose wrapped at 80 columns easily carries emphasis onto the next line: 44 of 60 real documents had it.
A regex-based converter paired the stranded ** with the next one and inverted the emphasis range. The
channel still returned HTTP 200, so nothing caught it.
input
공개 채널**이다. 글 내용이 아니라
**신분 공개 + 시점의 조합**이 판단 대상
broken
공개 채널<b>이다. 글 내용이 아니라
</b>신분 공개 …
mdwire
공개 채널이다. 글 내용이 아니라
<b>신분 공개 + 시점의 조합</b>이 판단 대상
The stranded ** is dropped and the ** at the start of the line opens a new bold, which is how CommonMark and Slack read it.
Test cases: emphasis-across-linebreak,
emphasis-wraps-line
Bold ending in a symbol, before a Korean particle
Korean attaches a particle right after the emphasis. When the bold ends in a parenthesis, quote, backtick or
period, CommonMark’s rules keep the closing ** from closing. On GitHub and Slack the asterisks stay as text,
and with several on one line the pairs cross and the ranges invert.
input
마감 전에 **설정(config)**을 바꾸고, **"배포 금지"**는 금요일까지 유지한다.
GitHub
마감 전에 <strong>설정(config)</strong>을 바꾸고, <strong>"배포 금지"</strong>는 금요일까지 유지한다.
Slack
마감 전에 **설정(config)**을 바꾸고, **"배포 금지"**는 금요일까지 유지한다.
Telegram
마감 전에 <b>설정(config)</b>을 바꾸고, <b>"배포 금지"</b>는 금요일까지 유지한다.
mdwire closes this bold. For GitHub it writes just those pairs as <strong> tags, which render regardless of
the rule. Slack has no tags, so mdwire puts an invisible U+2060 (word joiner) right before the closing ** —
the line looks the same, but Slack now renders the bold next release. Test case: punct-before-closing-emphasis
Tildes for ranges and approximations
Korean uses the tilde for ranges (5~6월) and approximations (~40km). GitHub (GFM) reads a single ~ as
strikethrough too, so two tildes in one paragraph strike everything between them.
input
주행 가능 거리는 ~40km 남았고 보정까지 ~22km 부족하다. 충전은 5~6월에 한다.
GitHub
주행 가능 거리는 \~40km 남았고 보정까지 \~22km 부족하다. 충전은 5\~6월에 한다.
For GitHub a literal ~ is escaped as \~, which shows as ~. A real strikethrough, ~~like this~~, is left
alone. Test case: tilde-is-not-strikethrough
Asterisks in masked numbers
A card number masked with asterisks was read as italics: part of the number turned italic, or asterisks disappeared.
input
결제 수단: 신한카드 1***-****-****-001* (본인 명의)
GitHub
결제 수단: 신한카드 1\*\*\*-\*\*\*\*-\*\*\*\*-001\* (본인 명의)
An asterisk where emphasis cannot open is treated as a literal character, and it is escaped for GitHub and Notion,
which would read it as syntax. Test case: masked-number-asterisks
An unpaired backtick run
A backtick run with no partner opened a code span that swallowed the rest of the block. The emphasis after it ended up trapped inside code.
input
앞부분 ``` 뒤에 **굵게** 가 온다
broken
앞부분 <code> 뒤에 **굵게** 가 온다</code>
Telegram
앞부분 ``` 뒤에 <b>굵게</b> 가 온다
If no closing run arrives before the block ends, the backticks go back to being text and the swallowed part is
read again, so its emphasis comes back. Test case: unpaired-backtick-run
An unclosed code fence
When the output hits a token limit or the last fence is forgotten, the code fence never closes. Sent as is, the channel rejects the message (400) or swallows the text after it as code.
input
```sh
tail -f app.log | grep ERROR
Telegram
<pre><code class="language-sh">tail -f app.log | grep ERROR</code></pre>
The fence is closed right before sending. Test case: unclosed-code-fence
A table with Korean text
Telegram has no table syntax, so a table goes out as a fixed-width block. Pad the columns by character count and a table with Korean text always misaligns: Hangul, Han characters and emoji take two cells on screen.
환경 | 재현 | 응답 시간
-------- | ------ | ---------
스테이징 | 예 | 120ms
프로덕션 | 예 | 340ms
로컬 | 아니오 | 15ms
Columns are measured by display width, and alignment such as :---: is kept. Slack and GitHub get the table as
is. Test case: korean-table
A tag cut at a part boundary
Splitting a long answer to fit the length limit put the cut in the middle of a tag such as <a href="…">. Each
part is one message, so both sides became broken HTML and the channel rejected the whole message. 8 of 2,289 real
documents hit this.
end of part 0
… 주소의 뒤집힌 꼴을 돌려준다 <a
start of part 1
href="https://…">src</a>
The cut is chosen from the document’s structure, not the rendered text. Only a point with no open markup can be a
boundary; otherwise the markup is closed before it and reopened in the next part. Test case: tag-across-part-boundary
Links in angle brackets
Text copied from Slack is full of <https://…> and <url|text> links: 124 of 256 real documents had them.
Escaping < on the way to Telegram HTML shows them as text, angle brackets and all.
input
슬랙에서 긁어 온 글은 <https://example.com/p|문서 보기> 꼴이다.
Telegram
슬랙에서 긁어 온 글은 <a href="https://example.com/p">문서 보기</a> 꼴이다.
Slack
슬랙에서 긁어 온 글은 [문서 보기](https://example.com/p) 꼴이다.
An angle-bracket link goes out as a link; the part after |, if any, is its text. Test case: angle-autolink