mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-24 00:51:19 +00:00
Slack rejects chat.postMessage with `invalid_blocks` when two block elements
in the same message share an `action_id`. The interview block builder
previously stamped every button with the constant `"interview.answer"`, so
any multi-button question (yes/no, confirmation, multiple_choice with N>1)
came back with:
{
"ok": false,
"error": "invalid_blocks",
"errors": [
"`action_id` \"interview.answer\" already exists [json-pointer:/blocks/1/elements/1/action_id]"
]
}
SlackService::handle_event in fabro-server swallows the error via
`if let Ok(posted) = self.client.post_message(...)`, so this failure was
invisible at INFO log levels.
Outbound: append a per-button suffix to the action_id:
- YesNo/Confirmation -> `interview.answer.yes` / `interview.answer.no`
- MultipleChoice -> `interview.answer.<index>`
The selected option key is still carried in the button `value` payload, so
the action_id only needs to be unique for routing — not semantic. Using the
option index (not the raw key) keeps suffixes short, ASCII, and dodges
Slack's 255-char action_id cap when author-supplied option keys are long.
Inbound: parse_interaction now matches both the legacy exact-prefix shape
(for in-flight messages posted by older builds) and the suffixed shape via a
pre-computed `ANSWER_ACTION_ID_PREFIX_DOT` constant (avoids per-parse
format! allocation). A lookalike like `interview.answers.yes` is correctly
rejected.
Tests:
- 4 new parse_interaction tests (suffixed yes/no, suffixed multi-choice,
legacy exact prefix, lookalike rejection)
- Updated existing block-builder tests to assert per-button uniqueness and
prove the option key still survives in the value payload
- Sync test that ANSWER_ACTION_ID_PREFIX_DOT matches the canonical prefix
- Updated one dispatch.rs and one connection.rs fixture to the suffixed
shape for clarity; left one legacy fixture in each to document the
backwards-compatibility branch
- 74/74 fabro-slack tests pass (was 69/69)
- `cargo +nightly-2026-04-14 fmt --check --all` and
`cargo +nightly-2026-04-14 clippy -p fabro-slack --all-targets -- -D warnings`
both clean
Verified end-to-end against a real Slack workspace: a multiple_choice
"Approve Plan" gate now renders correctly in the configured channel with
distinct [A] Approve / [R] Revise buttons.
|
||
|---|---|---|
| .. | ||
| crates | ||
| packages/fabro-api-client | ||