You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Shared parse_markdown: multi-backtick code spans, paragraph leading space, quadratic inputs (found in #283) #308
The shared parse_markdown (src/chat_sdk/shared/markdown_parser.py) has gaps that affect every adapter's inbound text. #283 recorded them while porting the Slack mrkdwn normalization, where every inbound Slack message passes through SlackFormatConverter.to_ast. None of them are Slack-specific, and none were introduced by #283.
Gaps
No multi-backtick code spans. The inline pattern only matches `x`. Remark (upstream) reads ```c``` and ``a`b`` as code spans.
Example: parse_markdown("a ```c``` b") gives the plain text "a ``c`` b", where upstream gives "a c b".
Slack example: > ```npm test```` gives `` "``npm test``" `` in message.text, where upstream gives "npm test"`.
A paragraph's leading space is kept. CommonMark strips it. "```x``` > note" gives "x\n\n > note", where upstream gives "x\n\n> note". Affected test: test_keeps_trailing_text_after_a_code_block_as_a_paragraph, which is adapted.
Quadratic time on some inputs. 10k characters of `x take about 0.6 s, 20k characters about 2.4 s, and 10k characters of *a about 1 s. The issue text for [4.41/SL4b] Slack inbound mrkdwn normalization, channel.post thread ids, Socket Mode retries (remainder of #209) #283 measured 25–37 s at 50k characters. slack_mrkdwn_to_markdown itself stays linear (about 1 ms for the same inputs). The cost is in parse_markdown, which runs on every inbound Slack message, and on other adapters' inbound text.
One more gap found while reviewing #307. Nested lists recurse once per level in parse_markdown, so "1. " * 1100 + "hi" and "- " * 1100 + "hi" raise RecursionError. This was already reachable on main, for example from Slack inbound text, which does not escape list markers. #307 caps only blockquote nesting, at 768 levels, because > now unescapes. A similar cap, or an iterative parser, would close the list case.
More findings from the #307 review round (PR head 868106e):
Fixed in #307 (no longer part of this issue): gap 2, the paragraph leading space. parse_markdown now drops each paragraph line's leading spaces/tabs and the paragraph's trailing ones. Also fixed there: mixed-marker thematic breaks (-_-), intraword _ emphasis, and nested links in link text.
New gaps for this issue:
4. No lazy blockquote continuation."> quoted\nreply" parses as a blockquote followed by a separate paragraph. Remark puts reply inside the quote paragraph. Slack example: ok\n> quoted\nreply gives message.text"ok\n\nquoted\n\nreply", where upstream gives "ok\n\nquoted\nreply".
5. \r is not a line ending.a <!here\r> b gives "a <!here\r> b @here". Remark splits on \r, which gives "a <!here\n\nb @here c" (its > opens a quote).
6. New quadratic triggers from the #307 fence rule. Before #307, a line starting with ``` always opened a code block that was parsed in O(n). Now such a line is inline text when its info string contains a backtick, and goes through the quadratic inline parser:
parse_markdown("```" + "a" * 8000)(16 KB) takes about 1.6 s.main` takes about 0 s.
parse_markdown("```x\n" * 8000)(48 KB) takes about 10 s.main` takes about 0.003 s.
This is the same per-byte cost as the existing `x trigger, but it is reached through new inputs. A possible fix: in _parse_inline, cache each pattern's next match by absolute position instead of re-searching every remaining slice. Match at the cursor with a lookbehind-free variant of the pattern, so the semantics at the slice start stay the same.
Activity
docs(slack): link shared-parser gaps to #308 (#283)
patrick-chinchill commentedon Oct 1, 2026
One more gap found while reviewing #307. Nested lists recurse once per level in
parse_markdown, so"1. " * 1100 + "hi"and"- " * 1100 + "hi"raiseRecursionError. This was already reachable onmain, for example from Slack inbound text, which does not escape list markers. #307 caps only blockquote nesting, at 768 levels, because>now unescapes. A similar cap, or an iterative parser, would close the list case.fix(markdown): CommonMark paragraph trim, single-marker breaks, no in…
patrick-chinchill commentedon Oct 1, 2026
More findings from the #307 review round (PR head 868106e):
Fixed in #307 (no longer part of this issue): gap 2, the paragraph leading space.
parse_markdownnow drops each paragraph line's leading spaces/tabs and the paragraph's trailing ones. Also fixed there: mixed-marker thematic breaks (-_-), intraword_emphasis, and nested links in link text.New gaps for this issue:
4. No lazy blockquote continuation.
"> quoted\nreply"parses as a blockquote followed by a separate paragraph. Remark putsreplyinside the quote paragraph. Slack example:ok\n> quoted\nreplygivesmessage.text"ok\n\nquoted\n\nreply", where upstream gives"ok\n\nquoted\nreply".5.
\ris not a line ending.a <!here\r> bgives"a <!here\r> b @here". Remark splits on\r, which gives"a <!here\n\nb @here c"(its>opens a quote).6. New quadratic triggers from the #307 fence rule. Before #307, a line starting with
```always opened a code block that was parsed in O(n). Now such a line is inline text when its info string contains a backtick, and goes through the quadratic inline parser:parse_markdown("```" + "a" * 8000)(16 KB) takes about 1.6 s.main` takes about 0 s.parse_markdown("```x\n" * 8000)(48 KB) takes about 10 s.main` takes about 0.003 s.This is the same per-byte cost as the existing
`xtrigger, but it is reached through new inputs. A possible fix: in_parse_inline, cache each pattern's next match by absolute position instead of re-searching every remaining slice. Match at the cursor with a lookbehind-free variant of the pattern, so the semantics at the slice start stay the same.fix(slack): inbound mrkdwn normalization, channel.post thread ids, So…