fix(messages): send parse=none on chat.update so links survive an edit (#106) (#120)
Closes #106, which you labelled `ready-for-pr`.
## What changed
`src/lib/slack-client.ts` — one parameter:
```ts
return this.request('chat.update', { channel, ts, text, parse: 'none' });
```
The comment above it records why it has to be explicit, since the value
looks redundant next to `chat.postMessage`: [the
docs](https://docs.slack.dev/reference/methods/chat.update) say `parse`
"Defaults to `client`, unlike `chat.postMessage`", and also that "If you
do not specify a value for this field, the original value set for the
message will be overwritten with the default" — so omitting it does not
inherit what the message was posted with.
## Verified on a real workspace
v0.9.0 from source against browser (xoxc/xoxd) auth, in a DM, same text
every time, reading `text` back with `conversations.history`:
| step | stored `text` |
|---|---|
| posted with `chat.postMessage` | `<https://example.com\|labelled
link>` |
| edited by v0.9.0 (no `parse`) | `<https://example.com\|labelled
link>` |
| edited with `parse: 'none'` | `<https://example.com\|labelled link>` |
Two details from the repro worth having on record:
- The corruption does not compound. Editing three more times leaves it
at `<…>` rather than escaping the `&`, so a message is broken once
and stays that way. It does not self-heal either — text posted as a link
never becomes a link again without a corrected edit.
- `<@U…>` mentions came through all three steps intact, so this is
specifically the `<url|label>` markup, not user references. `link_names`
also defaults to `none` on `chat.update` per the docs; I left it alone
since nothing in this repro needed it.
## Tests
`src/lib/slack-client.test.ts`: the existing `updateMessage` assertion
now includes `parse: 'none'` in the expected params, plus one case named
for the failure it prevents. The test double records `{method, params}`,
so it pins the wire call — which is where the bug was.
`bun test` 405 pass / 0 fail, `bun run type-check` clean. `pre-commit`
is not installed on this machine, so those hooks ran only in the form CI
runs them.
## Not covered
`chat.postMessage` is untouched: its `parse` default is already `none`,
and the repro confirms posted text arrives unescaped.
Co-authored-by: VibeXP Agent <hello@vibexp.io> V
Victor XH committed
6c0a885b55d9dbd6eb76fd9179129ef81c55b166
Parent: 8e6d9cc
Committed by GitHub <noreply@github.com>
on 8/20/2026, 7:51:16 AM