1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! Shared Slack `mrkdwn` formatting/escaping primitives.
//!
//! Why: the Slack `mrkdwn` escaping and code-fence helpers were born inside
//! trusty-mpm's `slack::formatter` (the inbound `/command` gateway renders
//! `CommandResult`s to `mrkdwn`). A second consumer now exists — the native
//! Slack MCP server in `trusty-channels` (epic #2636, ADR-0014) must apply the
//! identical escaping to untrusted channel/user text so a hostile message
//! cannot inject markup (e.g. a `<!channel>` broadcast span) into rendered
//! output. Re-implementing the escape rule in a second crate would risk the two
//! diverging on the exact substitution set, so the reusable, domain-free
//! primitives live here (the crate both already depend on) as the single source
//! of truth. The `CommandResult`-rendering `SlackFormatter` itself stays in
//! trusty-mpm because it is coupled to trusty-mpm's domain types; only these
//! pure helpers are shared.
//! What: [`mrkdwn_escape`] neutralises the three `mrkdwn`-significant characters;
//! [`code_block`] wraps text in a triple-backtick fence; [`code_inline`] wraps a
//! span in single backticks. All are pure `std` string operations — no
//! dependencies, no feature gate.
//! Test: the unit tests in this module cover each helper; trusty-mpm's
//! `slack::formatter::tests` and trusty-channels' handler tests exercise them
//! through their re-exports.
/// Escape the three `mrkdwn`-significant characters for a Slack message body.
///
/// Why: any backend- or user-controlled text interpolated into a Slack
/// `mrkdwn` reply (a session name, a channel message read back from Slack, an
/// error string) can carry Slack's special `<...>` link/mention/broadcast
/// markup. `<!channel>` broadcast-pings the whole channel and `<@U123>` mentions
/// a user; an unescaped message containing `<!channel>` would broadcast-ping the
/// channel from every rendered reply. Escaping the delimiters neutralises the
/// injection while leaving the visible text intact.
/// What: replaces `&` → `&`, `<` → `<`, `>` → `>`, in that order (so
/// the `&` produced by the `<`/`>` substitutions is never re-escaped). Slack
/// unescapes exactly these three entities in `mrkdwn` bodies, matching Slack's
/// own documented escaping contract for `chat.postMessage`.
/// Test: `mrkdwn_escape_escapes_ampersand_lt_gt`,
/// `mrkdwn_escape_neutralizes_channel_broadcast_span`.
/// Wrap text in a Slack triple-backtick code fence.
///
/// Why: pane output and captured command/message output should render
/// monospaced in Slack; `mrkdwn` uses triple backticks for that.
/// What: returns ```` ```\n<text>\n``` ````.
/// Test: `code_block_wraps_in_triple_backticks`.
/// Wrap a short span in a single-backtick inline-code run.
///
/// Why: ids, channel names, and other literal tokens read best monospaced
/// inline rather than as a full fenced block.
/// What: returns `` `<s>` ``. The caller is responsible for ensuring the span
/// contains no backticks (ids/names never do).
/// Test: `code_inline_wraps_in_single_backticks`.