Skip to content

feat: add Slack threading support (threadTs, autoThread, returnTs, dryRun, maxReports)#80

Open
smeiring wants to merge 4 commits into
ctrf-io:mainfrom
smeiring:feat/slack-threading-upstream
Open

feat: add Slack threading support (threadTs, autoThread, returnTs, dryRun, maxReports)#80
smeiring wants to merge 4 commits into
ctrf-io:mainfrom
smeiring:feat/slack-threading-upstream

Conversation

@smeiring
Copy link
Copy Markdown

@smeiring smeiring commented May 23, 2026

Summary

Adds message threading to all reporter commands, a SlackClient class with retry logic, and several quality-of-life options. All new options are optional and fully backwards-compatible — existing behaviour is unchanged.

Also fixes #47 — webhook 400 errors now include the Slack response body in the error message, making it much easier to diagnose issues like oversized payloads.

New options

Option CLI flag Env var Description
threadTs --thread-ts / -tt SLACK_THREAD_TS Reply to an existing Slack thread
returnTs --return-ts / -rt Print {"ts":"..."} to stdout for step chaining in CI
replyBroadcast --reply-broadcast / -rb Also broadcast the threaded reply to the channel
autoThread --auto-thread / -at SLACK_AUTO_THREAD Auto-thread individual failure details under a summary (default: false)
maxReports --max-reports / -mr Cap the number of individual failure messages (default: 10)
dryRun --dry-run / -dr SLACK_DRY_RUN Print payload to stdout instead of sending

Threading in CI pipelines

# Step 1 — post summary, capture ts
- name: Post test summary
  id: summary
  run: |
    ts=$(npx slack-ctrf results ctrf-report.json --return-ts | jq -r '.ts')
    echo "ts=$ts" >> $GITHUB_OUTPUT

# Step 2 — thread individual failures under it
- name: Post failures
  run: |
    npx slack-ctrf failed ctrf-report.json \
      --thread-ts ${{ steps.summary.outputs.ts }}

SlackClient refactor (src/client/slack-client.ts)

Replaces the ad-hoc helper functions with a unified SlackClient class that:

  • Handles both webhook and OAuth token paths behind a single sendMessage() API
  • Retries on ratelimited and transient errors (configurable via maxRetries / SLACK_MAX_RETRIES)
  • Produces friendly error messages for common Slack errors (channel_not_found, invalid_auth, not_in_channel)
  • Surfaces the full webhook response body on 4xx errors (fixes Ability to see the error in case of bad request #47): Slack webhook error (400): invalid_blocks

Security

Also includes npm audit fix resolving 3 transitive vulnerabilities (glob high, ajv moderate, brace-expansion moderate).

What's NOT in this PR

  • Emoji reactions (react, failedEmoji, passedEmoji) — can follow separately
  • Message updates (updateTs) — can follow separately

Test plan

  • npm run build:check — type-check passes
  • npm test — 47 tests pass (30 new tests for SlackClient and threading)
  • npm run lint:check && npm run format:check — CI checks pass
  • Manual: npx slack-ctrf results ctrf-report.json --dry-run — prints full Block Kit payload, does not send
  • Manual: npx slack-ctrf failed ctrf-report.json --thread-ts 1234567890.123456 --dry-run — payload contains "thread_ts": "1234567890.12346"
  • Manual: npx slack-ctrf results ctrf-report.json --return-ts --dry-run — prints {"ts":"dry-run-ts"} on stdout

🤖 Generated with Claude Code

Adds message threading to all reporter commands, a SlackClient class
with retry logic, and several quality-of-life options. All new options
are optional and backwards-compatible.

New options:
- threadTs / --thread-ts: reply to an existing Slack thread
- returnTs / --return-ts: output {"ts":"..."} for step chaining in CI
- replyBroadcast / --reply-broadcast: broadcast threaded reply to channel
- autoThread / --auto-thread: auto-thread failure details under a summary
  (default false — existing behaviour unchanged)
- maxReports / --max-reports: cap individual failure messages (default 10)
- dryRun / --dry-run: print payload to stdout instead of sending

SlackClient (src/client/slack-client.ts):
- Unifies webhook and OAuth token paths behind a single sendMessage() API
- Retries on ratelimited and transient errors (configurable maxRetries)
- Friendly error messages for channel_not_found, invalid_auth, not_in_channel

Also runs npm audit fix to resolve 3 transitive vulnerabilities:
- glob <10.5.0 (high) — CLI command injection, dev-only dependency
- ajv <6.14.0 (moderate) — ReDoS via $data option (not used)
- brace-expansion 5.0.2-5.0.5 (moderate) — DoS via large numeric ranges

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@smeiring smeiring changed the title feat: add Slack threading support feat: add Slack threading support (threadTs, autoThread, returnTs, dryRun, maxReports) May 23, 2026
smeiring and others added 2 commits May 23, 2026 02:38
When a webhook call returns a 4xx/5xx, @slack/webhook throws an error
with statusCode and body properties. Previously formatError fell back to
error.message which only contained "An HTTP protocol error occurred:
statusCode = 400", discarding the actual Slack error detail (e.g.
"invalid_blocks", "text_too_long").

Now formatError checks for statusCode + body and produces:
  "Slack webhook error (400): invalid_blocks"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…docs

- Fix auto-thread default: it is opt-in (false by default), not the default
- Remove the incorrect "Auto-Threading (Default)" section
- Add Dry Run and Limit Failure Reports sections
- Restructure env vars into a dedicated section; remove SLACK_TITLE,
  SLACK_FAILED_EMOJI, SLACK_PASSED_EMOJI (not on this branch)
- Remove --update-ts and --react options from Options list (separate PRs)
- Fix --auto-thread default description in Options list (true → false)
- Add --max-retries to Options list
- Add Threading Support and Dry Run to Features list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ability to see the error in case of bad request

1 participant