Skip to content

Slack rich_blocks: blank-line-separated ordered lists render every item as "1." #57076

Description

@skapoor-coatue

Bug description

With platforms.slack.extra.rich_blocks: true, an ordered (numbered) Markdown list whose items are separated by blank lines renders in Slack with every item numbered "1." instead of 1, 2, 3, …

Blank lines between list items are the normal shape of LLM-authored content (e.g. a scheduled briefing with a numbered "Top of Mind" section), so this hits real output, not just contrived input.

Root cause

In plugins/platforms/slack/block_kit.py, render_blocks() groups a contiguous run of list lines into a single rich_text_list element (block_kit.py#L437). The run loop only continues across (a) another bullet/ordered line or (b) an indented continuation line:

if _BULLET_RE.match(line) or _ORDERED_RE.match(line):
    ...
    while i < n:
        bm = _BULLET_RE.match(lines[i])
        om = _ORDERED_RE.match(lines[i])
        if bm: ...
        elif om: ...
        elif lines[i].strip() and lines[i].startswith((" ", "\t")) and items:
            # continuation line of the previous item
            ...
        else:
            break   # <-- a blank line lands here and ends the run

A blank line (lines[i].strip() == "") falls through to else: break, so the run ends. The next numbered item then starts a new single-item rich_text_list. Slack numbers each rich_text_list independently starting at 1, so N blank-separated items become N separate lists, each rendering "1.".

_list_block itself is correct — the items never reach it as one run.

Steps to reproduce

  1. Enable rich blocks:
    platforms:
      slack:
        extra:
          rich_blocks: true
  2. Have the agent post a numbered list with blank lines between items, e.g.:
    1. First item.
    
    1. Second item.
    
    1. Third item.
    
    (1./1./1. or 1./2./3. — Markdown renders both as 1,2,3; the bug is independent of the authored numbers.)

Minimal renderer-level repro:

from plugins.platforms.slack.block_kit import render_blocks

blocks = render_blocks("1. alpha\n\n1. beta\n\n1. gamma", mrkdwn_fn=lambda s: s)
lists = [(e["style"], e["indent"], len(e["elements"]))
         for b in blocks for e in b.get("elements", [])
         if e.get("type") == "rich_text_list"]
print(len(blocks), lists)

Expected

One rich_text_list with 3 items → Slack renders 1, 2, 3.

2 [('ordered', 0, 1), ...]   # contiguous input already works: 1. a / 2. b / 3. c → one list

Actual

Three separate single-item rich_text_list elements → Slack renders 1, 1, 1.

3 [('ordered', 0, 1), ('ordered', 0, 1), ('ordered', 0, 1)]

Contiguous items (no blank lines) render correctly, confirming blank-line handling is the trigger. Bullet lists are visually unaffected (no numbering to reset), so this is specific to ordered lists.

Suggested fix

Treat a blank line inside a list run as a soft separator that does not terminate the run when the next non-blank line is another list item of the same (indent, ordered) kind — i.e. look past a single blank line before breaking, rather than breaking on it. That keeps a blank-separated numbered list as one rich_text_list, preserving 1, 2, 3.

Environment

  • hermes-agent==0.18.0 (PyPI wheel)
  • Platform: Slack (Socket Mode), rich_blocks: true

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/gatewayGateway runner, session dispatch, deliveryplatform/slackSlack app adaptertype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions