Skip to content

Extract Slack notifications and preserve queued transport APIs - #650

Merged
binaryfire merged 26 commits into
0.4from
extract-slack-notification-channel
Oct 6, 2026
Merged

binaryfire merged 26 commits into
0.4from
extract-slack-notification-channel

Conversation

@binaryfire

@binaryfire binaryfire commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Slack notifications currently live inside core Notifications, with a layout and channel names that differ from Laravel's Slack notification package. This makes upstream maintenance harder and makes Slack-specific dependencies part of the core package.

This change extracts Slack into hypervel/slack-notification-channel, aligns its layout and channel names with upstream, and fixes queued notification events that contain HTTP transport objects.

Slack package

  • Move the API and webhook channels, message builders, Block Kit types, routes, and tests into the new package. Existing builder namespaces remain unchanged.
  • Use the upstream Slack\SlackChannel, root SlackNotificationRouterChannel, and SlackChannelServiceProvider names.
  • Register the optional channel through package discovery. Core Notifications no longer creates the Slack driver directly.
  • Send Slack API requests through Hypervel's HTTP client using the slack-notifications connection. Connection handling is shared; tokens, recipients, and payloads remain local to each request.
  • Keep webhook delivery on Guzzle so existing per-message HTTP options remain available.
  • Declare the direct package dependencies and add the Slack package to Horizon's requirements.

The extraction also preserves optional payload values that truthy filtering previously dropped: explicit false unfurl flags, strings containing 0, and epoch-zero attachment timestamps. Unset fields and empty optional arrays remain omitted. The webhook message's channel setter accepts null, allowing Horizon notifications to use the webhook's default channel.

Queued notification events

NotificationDelivered, NotificationSent, and NotificationFailed can contain HTTP responses or exceptions with body streams. PHP cannot serialize those streams when queueing an event listener. On success, that can fail after Slack has accepted the message and cause a notification retry to send it again. On failure, it can replace the original delivery error with a serialization error.

Prepare recognized transport state when the notification event is serialized, then restore the normal response and exception objects before the listener runs. This preserves the usual APIs, model restoration, headers, request targets, previous exceptions, response metadata, and supported buffer state. Synchronous listeners keep the original objects, and generic queue serialization is unchanged.

Objects with custom serialization hooks retain PHP's native behavior, including custom wakeup ordering. Default writable memory buffers and closed or detached built-in streams are supported. Other body types fail clearly without being consumed; the documentation explains how to handle these events synchronously and dispatch a job with only the required data.

Prepared queued exception copies omit stack-trace argument values, which can contain values PHP cannot serialize. The original exception remains unchanged. Existing Symfony mail exception handling is preserved.

Verification

  • composer lint:fix and composer analyse passed, including the committed type fixtures.
  • Targeted Notifications, Slack notification channel, and Horizon suites passed, along with HTTP connection, queued listener, model serialization, and package manifest checks.
  • SQLite coverage passed through the real serializing listener queue, confirming database model restoration alongside restored transport objects.
  • Independent package installations passed with Guzzle 7 / PSR-7 2 and Guzzle 8 / PSR-7 3. Checks covered HTTP success and failures, POST connection failures, and both exception trace settings.
  • Regression coverage includes webhook and API delivery, optional payload values, native serialization hooks, buffer boundaries, original exception identity, and closed response bodies.
  • Benchmarks showed small buffered response jobs taking roughly 0.08 ms for serialization and restoration, with no retained memory growth in repeated failure round trips. Work and temporary memory grow with body size; synchronous listeners do not prepare transport snapshots.
  • Composer validation and diff checks passed.

Review in cubic Turn on auto-fix

Note

Extract Slack notifications into hypervel/slack-notification-channel and preserve queued transport APIs

  • Moves all Slack channel, message, route, and BlockKit code out of the notifications package into a standalone hypervel/slack-notification-channel package with its own composer manifest, SlackChannelServiceProvider, and MIT license. The notifications package no longer ships a slack driver; resolving slack without the new package installed now throws InvalidArgumentException.
  • Adds TransportSnapshot and SerializesTransport so queued NotificationSent, NotificationDelivered, and NotificationFailed events serialize HTTP responses, exceptions, and streams and restore the original transport objects on the queued listener side.
  • Rewrites SlackChannel to use the Hypervel HTTP client factory with a named slack-notifications connection instead of an injected Guzzle client and config repository.
  • Fixes falsey-value filtering: BlockKit blocks, elements, and webhook payloads now keep false and 0 values while still omitting null, empty strings, and empty arrays. Adds regression tests for zero-valued block_id and button value.
  • Behavioral Change: SlackAttachment.timestamp and SlackMessage unfurl properties now default to null instead of 0/false; SlackWebhookChannel.send no longer throws RuntimeException when a routed notification lacks toSlack (missing-method error instead); notifications composer.json now requires guzzlehttp/psr7, hypervel/http, and laravel/serializable-closure and no longer requires ext-mbstring.
📊 Macroscope summarized c5f5580. 27 files reviewed, 2 issues evaluated, 1 issue filtered, 1 comment posted

🗂️ Filtered Issues

src/slack-notification-channel/src/Channels/SlackWebhookChannel.php — 1 comment posted, 2 evaluated, 1 filtered
  • line 31: send() now checks the Slack route before verifying that the notification implements toSlack. A notification configured for this channel but missing toSlack will return null whenever a recipient has no Slack route, whereas the previous implementation raised its explicit RuntimeException; queued/bulk delivery therefore treats this misconfiguration as a successful no-op and loses the diagnostic. [ Out of scope (triage) ]

Summary by CodeRabbit

  • New Features
    • Added a standalone Slack notification channel with automatic registration and configurable HTTP connection settings.
    • Queued notification listeners can now access restored HTTP responses and exceptions after serialization.
  • Bug Fixes
    • Slack message payloads now preserve meaningful falsey values, omit unset optional fields, and support webhook messages without a channel override.
  • Documentation
    • Updated Slack setup guidance and documented HTTP connection options and queued notification listener behavior.

Declare hypervel/slack-notification-channel as an optional package with its direct runtime dependencies, standard license, and upstream documentation links.

Wire its shared Notifications namespace and provider discovery into monorepo development. The Slack provider registers the channel through ChannelManager and owns the fixed slack-notifications HTTP connection; it does not add a core default provider or redundant service bindings.
…kage

Move the Slack message contracts, route value object, and event metadata out of core Notifications while preserving their public namespaces and behavior.

Keep these shared types beside the channel and builders that use them. Their names and layout continue to follow the upstream Slack notification package.
Move the confirmation object into the Slack channel package without changing its public API or payload behavior. Keep its existing validation and payload tests under the upstream unit-test layout.
Move the plain-text object into the Slack channel package while retaining its length validation, emoji handling, and payload shape. Relocate its existing tests into the upstream unit-test layout.
Move the text object into the Slack channel package without changing its plain-text or markdown APIs. Retain the existing tests for payload formatting and validation.
Move action blocks and their tests into the Slack channel package. Filter optional fields by absence rather than truthiness so a block ID of 0 is retained.

Keep the upstream block structure and existing element validation, and cover the meaningful zero-valued identifier alongside the retained payload tests.
Move context blocks and their tests into the Slack channel package. Preserve a block ID of 0 when formatting optional fields.

Correct the element annotation to include TextObject, which is added by the text methods alongside element-contract implementations. This changes the annotation without narrowing the public extension surface.
Move divider blocks and their tests into the Slack channel package. Preserve an explicitly supplied block ID of 0 while continuing to omit absent optional fields.
Move header blocks and their tests into the Slack channel package. Retain the existing text validation and formatting, and preserve an explicitly supplied block ID of 0.
Move image blocks and their tests into the Slack channel package. Preserve a block ID of 0 while retaining the existing image, title, and alternative-text payload behavior.
Move section blocks and their tests into the Slack channel package. Preserve a block ID of 0 while continuing to omit absent values and empty field lists.

Retain the existing text, accessory, and field validation and payload coverage.
Move button elements and their tests into the Slack channel package. Preserve value and accessibility-label strings containing 0 instead of dropping them through truthy filtering.

Keep the upstream button API, validation, and payload structure, with coverage for these valid optional strings.
Move image elements into the Slack channel package without changing their public API or payload shape. Retain the existing validation and serialization tests under the upstream unit-test layout.
Move the select-element base class and generated-ID trait beside their consumers in the Slack channel package.

Preserve the upstream nested Elements/Traits layout and the existing shared select behavior. No new identifier mechanism or compatibility alias is introduced.
Move this select type into the Slack channel package while preserving its public API, validation, and payload behavior.

Retain its existing regression coverage under the upstream unit-test layout so future upstream changes can be compared directly.
Move this select type into the Slack channel package while preserving its public API, validation, and payload behavior.

Retain its existing regression coverage under the upstream unit-test layout so future upstream changes can be compared directly.
Move this select type into the Slack channel package while preserving its public API, validation, and payload behavior.

Retain its existing regression coverage under the upstream unit-test layout so future upstream changes can be compared directly.
Move the Block Kit Slack message builder into the Slack channel package without changing its message API or payload behavior.

Move its feature tests, named notification fixtures, and shared testbench setup with it. Keep the existing builder coverage and use the package provider when exercising delivery through the framework.
Move the webhook message and attachment builders into the Slack channel package while preserving their public namespaces.

Use nullable defaults for optional unfurl flags and timestamps so unset values remain distinct from explicit false or epoch-zero values. Allow the webhook channel setter to accept null, which lets a webhook use its configured default channel without changing existing string calls.
Move SlackWebhookChannel into the optional Slack channel package and retain its Guzzle transport and per-message HTTP options.

Filter optional payload fields by absence rather than truthiness so explicit false unfurl flags, zero-valued strings, and epoch-zero attachment timestamps are retained. Continue to omit empty optional arrays and unset values, and retain the no-route early return.

Move the existing webhook regression suite with the channel and extend coverage for these payload cases, default omission, and null-channel routing.
Move the modern API channel to Slack/SlackChannel, matching the upstream name and namespace.

Use the HTTP factory and the slack-notifications connection for JSON requests, bearer tokens, normal HTTP errors, and standard HTTP response APIs. Keep route and payload resolution per send so connection reuse does not retain recipient state.

Preserve Slack API error handling and remove the redundant notification-method guard. Move and extend the channel tests for routing, token isolation, fake delivery, HTTP failures, and Slack API failures.
Move SlackNotificationRouterChannel to the upstream root namespace and retain the container contract used to resolve its channels.

Keep URL and URI routes on the webhook channel, other routes on the modern API channel, and false routes disabled. Remove the core Slack driver factory so discovery of the optional package owns registration.

Move the router tests and verify that core Notifications alone does not provide the Slack driver.
Declare the HTTP response, Guzzle message and transfer-stat, and serializable-closure dependencies used directly by notification-event serialization.

Move the Slack-only mbstring requirement and Slack-specific README material out of core Notifications. Keep package metadata tests aligned with the actual direct dependencies and the core notification provider.
Queued terminal notification events can contain HTTP responses or exceptions with body streams that PHP cannot serialize. A successful delivery can then fail while queueing its listener, and a failure listener can replace the original delivery error.

Prepare recognized transport state only at the notification-event serialization boundary and restore the normal response and exception classes before listeners run. Preserve model restoration, direct shared transport references, subclass state, request targets, headers, buffer state, and previous exceptions.

Keep native custom serialization contracts under PHP control, including user-defined wakeup hooks. Preserve default writable memory buffers and closed or detached built-in streams; reject unsupported body types without consuming them. Omit trace argument values only in prepared queued exception copies, leaving the original exception unchanged.

Cover actual serializing listener jobs, channel success and failure events, sensitive arguments, native hook ordering, buffer boundaries, and database model restoration. Generic queue serialization and existing Symfony mail exception handling remain unchanged.
Declare Horizon's direct dependency on hypervel/slack-notification-channel now that the builders and delivery channels live outside core Notifications.

Verify the package dependency and the supported webhook route without an explicit channel. The regression confirms Horizon retains its attachment payload while allowing the webhook's configured channel default.
Document installation of hypervel/slack-notification-channel and its automatically registered named HTTP connection, including a boot-time timeout configuration example.

Explain that queued terminal-event listeners keep the usual response and exception APIs with default HTTP transports. Describe the queued-copy trace argument limitation and give direct guidance for notification values that cannot be serialized, without exposing the internal snapshot implementation.
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: hypervel/components/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9ab8df62-8e9e-44fd-9555-d7d8f44c0939
📥 Commits

Reviewing files that changed from the base of the PR and between b4bd09f and c5f5580.

📒 Files selected for processing (78)
  • composer.json
  • src/docs/notifications.md
  • src/horizon/composer.json
  • src/notifications/README.md
  • src/notifications/composer.json
  • src/notifications/src/ChannelManager.php
  • src/notifications/src/Concerns/SerializesTransport.php
  • src/notifications/src/Events/NotificationDelivered.php
  • src/notifications/src/Events/NotificationFailed.php
  • src/notifications/src/Events/NotificationSent.php
  • src/notifications/src/Support/TransportReference.php
  • src/notifications/src/Support/TransportSnapshot.php
  • src/slack-notification-channel/LICENSE.md
  • src/slack-notification-channel/README.md
  • src/slack-notification-channel/composer.json
  • src/slack-notification-channel/src/Channels/SlackWebhookChannel.php
  • src/slack-notification-channel/src/Messages/SlackAttachment.php
  • src/slack-notification-channel/src/Messages/SlackAttachmentField.php
  • src/slack-notification-channel/src/Messages/SlackMessage.php
  • src/slack-notification-channel/src/Slack/BlockKit/Blocks/ActionsBlock.php
  • src/slack-notification-channel/src/Slack/BlockKit/Blocks/ContextBlock.php
  • src/slack-notification-channel/src/Slack/BlockKit/Blocks/DividerBlock.php
  • src/slack-notification-channel/src/Slack/BlockKit/Blocks/HeaderBlock.php
  • src/slack-notification-channel/src/Slack/BlockKit/Blocks/ImageBlock.php
  • src/slack-notification-channel/src/Slack/BlockKit/Blocks/SectionBlock.php
  • src/slack-notification-channel/src/Slack/BlockKit/Composites/ConfirmObject.php
  • src/slack-notification-channel/src/Slack/BlockKit/Composites/PlainTextOnlyTextObject.php
  • src/slack-notification-channel/src/Slack/BlockKit/Composites/TextObject.php
  • src/slack-notification-channel/src/Slack/BlockKit/Elements/ButtonElement.php
  • src/slack-notification-channel/src/Slack/BlockKit/Elements/ImageElement.php
  • src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/SelectElement.php
  • src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/SelectOption.php
  • src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/StaticSelectElement.php
  • src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/UsersSelectElement.php
  • src/slack-notification-channel/src/Slack/BlockKit/Elements/Traits/GeneratesDefaultIds.php
  • src/slack-notification-channel/src/Slack/Contracts/AccessoryContract.php
  • src/slack-notification-channel/src/Slack/Contracts/BlockContract.php
  • src/slack-notification-channel/src/Slack/Contracts/ElementContract.php
  • src/slack-notification-channel/src/Slack/Contracts/ObjectContract.php
  • src/slack-notification-channel/src/Slack/EventMetadata.php
  • src/slack-notification-channel/src/Slack/SlackChannel.php
  • src/slack-notification-channel/src/Slack/SlackMessage.php
  • src/slack-notification-channel/src/Slack/SlackRoute.php
  • src/slack-notification-channel/src/SlackChannelServiceProvider.php
  • src/slack-notification-channel/src/SlackNotificationRouterChannel.php
  • tests/Horizon/Notifications/LongWaitDetectedTest.php
  • tests/Horizon/PackageMetadataTest.php
  • tests/Integration/Notifications/Database/Sqlite/NotificationTransportModelTest.php
  • tests/Notifications/Fixtures/LegacySerializableTransportException.php
  • tests/Notifications/NotificationChannelManagerTest.php
  • tests/Notifications/NotificationTransportSerializationTest.php
  • tests/Notifications/PackageMetadataTest.php
  • tests/Notifications/Slack/SlackWebApiChannelTest.php
  • tests/Notifications/Slack/TestCase.php
  • tests/SlackNotificationChannel/NotificationSlackChannelTest.php
  • tests/SlackNotificationChannel/NotificationTransportQueueTest.php
  • tests/SlackNotificationChannel/PackageMetadataTest.php
  • tests/SlackNotificationChannel/Slack/Feature/SlackChannelTest.php
  • tests/SlackNotificationChannel/Slack/Feature/SlackMessageTest.php
  • tests/SlackNotificationChannel/Slack/Fixtures/SlackChannelTestNotifiable.php
  • tests/SlackNotificationChannel/Slack/Fixtures/SlackChannelTestNotification.php
  • tests/SlackNotificationChannel/Slack/TestCase.php
  • tests/SlackNotificationChannel/Slack/Unit/Blocks/ActionsBlockTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Blocks/ContextBlockTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Blocks/DividerBlockTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Blocks/HeaderBlockTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Blocks/ImageBlockTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Blocks/SectionBlockTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Composites/ConfirmObjectTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Composites/PlainTextOnlyTextObjectTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Composites/TextObjectTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Elements/ButtonElementTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Elements/ImageElementTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Elements/Selects/SelectOptionTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Elements/Selects/StaticSelectElementTest.php
  • tests/SlackNotificationChannel/Slack/Unit/Elements/Selects/UsersSelectElementTest.php
  • tests/SlackNotificationChannel/SlackChannelServiceProviderTest.php
  • tests/SlackNotificationChannel/SlackNotificationRouterChannelTest.php
💤 Files with no reviewable changes (3)
  • tests/Notifications/Slack/SlackWebApiChannelTest.php
  • src/notifications/src/ChannelManager.php
  • tests/Notifications/Slack/TestCase.php

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change adds transport-aware serialization for queued notification events and moves Slack notification delivery into a separately registered package. It also updates Slack HTTP handling, payload serialization, Composer metadata, documentation, and related tests.

Changes

Notification transport serialization

Layer / File(s) Summary
Transport snapshots and queued events
src/notifications/src/Concerns/*, src/notifications/src/Support/*, src/notifications/src/Events/*, tests/Notifications/NotificationTransportSerializationTest.php, tests/Notifications/Fixtures/*, tests/Integration/Notifications/Database/Sqlite/*, tests/SlackNotificationChannel/NotificationTransportQueueTest.php, src/docs/notifications.md, src/notifications/composer.json, tests/Notifications/PackageMetadataTest.php
Notification event serialization snapshots response or exception state alongside model data. Snapshot capture and restoration handle supported transport objects, stream state, object references, closures, and exception traces. Tests cover serialization round trips, queued listeners, restored models, and unsupported streams.

Slack notification channel

Layer / File(s) Summary
Package integration and HTTP delivery
composer.json, src/horizon/composer.json, src/slack-notification-channel/composer.json, src/slack-notification-channel/src/Slack/*, src/slack-notification-channel/src/SlackChannelServiceProvider.php, src/slack-notification-channel/src/SlackNotificationRouterChannel.php, src/notifications/src/ChannelManager.php, src/notifications/README.md, src/docs/notifications.md, tests/SlackNotificationChannel/SlackChannelServiceProviderTest.php, tests/SlackNotificationChannel/SlackNotificationRouterChannelTest.php, tests/SlackNotificationChannel/Slack/Feature/SlackChannelTest.php, tests/SlackNotificationChannel/PackageMetadataTest.php, tests/Notifications/NotificationChannelManagerTest.php, tests/Notifications/Slack/*, tests/Horizon/*
Composer autoloading and provider registration include the Slack channel package. The Notifications channel manager no longer provides its built-in Slack driver. Slack API requests use the named HTTP connection, and routing continues to select the webhook or API channel. Package, provider, route, and delivery tests cover the updated setup.
Slack message and block payloads
src/slack-notification-channel/src/Channels/*, src/slack-notification-channel/src/Messages/*, src/slack-notification-channel/src/Slack/BlockKit/*, tests/SlackNotificationChannel/NotificationSlackChannelTest.php, tests/SlackNotificationChannel/Slack/Feature/*, tests/SlackNotificationChannel/Slack/Fixtures/*, tests/SlackNotificationChannel/Slack/Unit/*
Slack messages and webhook payloads use nullable optional values and explicit filters. Block Kit serialization retains values such as the string '0'. Slack message, webhook, and Block Kit tests use the package test namespace and cover those payloads.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant NotificationEvent
  participant SerializesTransport
  participant TransportSnapshot
  participant Queue
  participant QueuedListener
  NotificationEvent->>SerializesTransport: serialize event state
  SerializesTransport->>TransportSnapshot: capture response or exception
  TransportSnapshot-->>SerializesTransport: return transport snapshot
  SerializesTransport->>Queue: provide serialized event state
  Queue->>SerializesTransport: restore serialized event state
  SerializesTransport->>TransportSnapshot: restore transport object
  SerializesTransport->>QueuedListener: provide restored event
Loading

Merge Risk: ⚪ Minimal · up to c5f55

The reviewed changes are mergeable after normal checks; no material notification or Slack delivery failure was established.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.94% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 178 functions across 50 files. (8 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main changes: extracting Slack notifications and preserving queued transport APIs. It is concise and specific.
Description check ✅ Passed The description explains the problem and changes, gives supporting test and benchmark details, and reports verification results. It does not use the template’s contribution-type or before-submitting s…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 44.94% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 178 functions across 50 files. (8 skipped: 8 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@binaryfire

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@binaryfire

Copy link
Copy Markdown
Member Author

@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 6, 2026

Copy link
Copy Markdown

@cubic-dev-ai review

@binaryfire cubic can't start this review because your workspace has reached its free monthly review limit. cubic has reviewed 124,545 of the 120,000 allowed lines of code this month. Reviews resume on 10 October 2026 (in 4 days). Paid plans include much higher monthly review limits. Upgrade now to resume reviews.

To help optimise your usage, you can tune cubic to get the most out of your usage limits:

Learn more →

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Extract Slack notifications and preserve queued transport APIs

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Extract Slack into an optional package with upstream-aligned channels and independent
 dependencies.
• Preserve explicit Slack payload values and deliver API requests through Hypervel’s HTTP client.
• Restore response and exception APIs in queued notification listeners without changing synchronous
 delivery.
Diagram

graph TD
  Provider["Slack provider"] --> Manager["Channel manager"] --> Router["Slack router"] --> API["API channel"] --> HTTP["HTTP transports"]
  Router --> Webhook["Webhook channel"] --> HTTP
  Manager --> Events["Notification events"] --> Snapshot["Transport snapshot"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Queue compact notification result DTOs
  • ➕ Avoids reflective reconstruction and buffering entire HTTP bodies.
  • ➕ Produces smaller, more predictable queue payloads.
  • ➖ Changes listener-facing response and exception APIs.
  • ➖ Requires consumers to identify and copy the fields they need.
2. Normalize transport objects before dispatch
  • ➕ Keeps serialization logic out of event classes.
  • ➖ Changes objects seen by synchronous listeners.
  • ➖ Risks discarding transport-specific metadata and exception relationships.

Recommendation: The event-scoped snapshot is the best fit for the stated compatibility goal: synchronous listeners retain original objects, while queued listeners recover their usual APIs. Keep its supported-stream boundary explicit and review body-size costs; applications with unbounded bodies should queue compact data instead.

Files changed (76) +2399 / -231

Enhancement (2) +58 / -28
SlackChannel.phpSend Slack API messages through Hypervel HTTP +24/-28

Send Slack API messages through Hypervel HTTP

• Renames the API channel to the upstream-aligned SlackChannel and uses a named Hypervel HTTP connection. Per-request tokens and payloads stay local, and HTTP or explicit Slack API failures remain detectable.

src/slack-notification-channel/src/Slack/SlackChannel.php

SlackChannelServiceProvider.phpRegister the optional Slack driver and HTTP connection +34/-0

Register the optional Slack driver and HTTP connection

• Extends the notification manager when resolved and registers the shared slack-notifications connection.

src/slack-notification-channel/src/SlackChannelServiceProvider.php

Bug fix (16) +437 / -29
SerializesTransport.phpIntegrate transport snapshots with event model serialization +54/-0

Integrate transport snapshots with event model serialization

• Wraps existing model serialization to capture successful responses or failed-event exceptions and restore them before queued listeners run.

src/notifications/src/Concerns/SerializesTransport.php

NotificationDelivered.phpPrepare delivered-event responses for queues +2/-2

Prepare delivered-event responses for queues

• Uses transport-aware serialization while retaining model serialization.

src/notifications/src/Events/NotificationDelivered.php

NotificationFailed.phpPrepare failed-event exceptions for queues +2/-2

Prepare failed-event exceptions for queues

• Uses transport-aware serialization so queued listeners can inspect supported delivery failures.

src/notifications/src/Events/NotificationFailed.php

NotificationSent.phpPrepare sent-event responses for queues +2/-2

Prepare sent-event responses for queues

• Uses transport-aware serialization while retaining model serialization.

src/notifications/src/Events/NotificationSent.php

TransportReference.phpIdentify shared objects within transport snapshots +18/-0

Identify shared objects within transport snapshots

• Adds an internal indexed reference for restoring shared objects and cycles.

src/notifications/src/Support/TransportReference.php

TransportSnapshot.phpCapture and restore supported HTTP transport graphs +341/-0

Capture and restore supported HTTP transport graphs

• Snapshots responses, requests, exceptions, closures, and supported memory streams without consuming original bodies. Restores object relationships and metadata, preserves native serialization hooks, and rejects unsupported streams explicitly.

src/notifications/src/Support/TransportSnapshot.php

SlackWebhookChannel.phpRetain Guzzle webhooks and explicit payload values +6/-11

Retain Guzzle webhooks and explicit payload values

• Keeps per-message Guzzle options while preserving explicit false and zero-like values and omitting unset optional fields.

src/slack-notification-channel/src/Channels/SlackWebhookChannel.php

SlackAttachment.phpDistinguish unset timestamps from epoch zero +1/-1

Distinguish unset timestamps from epoch zero

• Makes the default timestamp null so an explicitly assigned zero remains in webhook payloads.

src/slack-notification-channel/src/Messages/SlackAttachment.php

SlackMessage.phpSupport unset unfurl flags and nullable webhook channels +3/-3

Support unset unfurl flags and nullable webhook channels

• Uses null defaults for optional unfurl flags and permits clearing a channel override so the webhook default applies.

src/slack-notification-channel/src/Messages/SlackMessage.php

ActionsBlock.phpPreserve zero-like action block IDs +1/-1

Preserve zero-like action block IDs

• Filters only unset or empty optional values when constructing the Block Kit payload.

src/slack-notification-channel/src/Slack/BlockKit/Blocks/ActionsBlock.php

ContextBlock.phpPreserve context block IDs +2/-2

Preserve context block IDs

• Keeps zero-like IDs and clarifies that context elements can include text objects.

src/slack-notification-channel/src/Slack/BlockKit/Blocks/ContextBlock.php

DividerBlock.phpPreserve zero-like divider IDs +1/-1

Preserve zero-like divider IDs

• Stops truthy filtering from removing an explicitly assigned zero-like ID.

src/slack-notification-channel/src/Slack/BlockKit/Blocks/DividerBlock.php

HeaderBlock.phpPreserve zero-like header IDs +1/-1

Preserve zero-like header IDs

• Stops truthy filtering from removing an explicitly assigned zero-like ID.

src/slack-notification-channel/src/Slack/BlockKit/Blocks/HeaderBlock.php

ImageBlock.phpPreserve optional image block values +1/-1

Preserve optional image block values

• Keeps zero-like IDs while omitting unset or empty optional values.

src/slack-notification-channel/src/Slack/BlockKit/Blocks/ImageBlock.php

SectionBlock.phpPreserve section IDs without emitting empty fields +1/-1

Preserve section IDs without emitting empty fields

• Keeps zero-like IDs and continues omitting empty optional field arrays.

src/slack-notification-channel/src/Slack/BlockKit/Blocks/SectionBlock.php

ButtonElement.phpPreserve zero-like button values +1/-1

Preserve zero-like button values

• Filters only unset and empty optional values, retaining explicit zero-like accessibility labels.

src/slack-notification-channel/src/Slack/BlockKit/Elements/ButtonElement.php

Refactor (19) +9 / -16
ChannelManager.phpRemove the built-in Slack driver +0/-9

Remove the built-in Slack driver

• Core no longer resolves Slack directly; the optional package provider registers it instead.

src/notifications/src/ChannelManager.php

SlackAttachmentField.phpMove legacy Slack attachment fields +0/-0

Move legacy Slack attachment fields

• Relocates the field builder into the optional package without changing its behavior.

src/slack-notification-channel/src/Messages/SlackAttachmentField.php

ConfirmObject.phpMove Slack confirmation composite +0/-0

Move Slack confirmation composite

• Relocates the confirmation builder to the optional package without changing its public API.

src/slack-notification-channel/src/Slack/BlockKit/Composites/ConfirmObject.php

PlainTextOnlyTextObject.phpMove plain-text-only composite +0/-0

Move plain-text-only composite

• Relocates the existing plain-text builder without changing its behavior.

src/slack-notification-channel/src/Slack/BlockKit/Composites/PlainTextOnlyTextObject.php

TextObject.phpMove Slack text composite +0/-0

Move Slack text composite

• Relocates the existing text builder without changing its public namespace.

src/slack-notification-channel/src/Slack/BlockKit/Composites/TextObject.php

ImageElement.phpMove Block Kit image element +0/-0

Move Block Kit image element

• Relocates the image element into the optional Slack package.

src/slack-notification-channel/src/Slack/BlockKit/Elements/ImageElement.php

SelectElement.phpMove base select element +0/-0

Move base select element

• Relocates the select builder into the optional package while preserving its namespace and behavior.

src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/SelectElement.php

SelectOption.phpMove select option builder +0/-0

Move select option builder

• Relocates the option builder without changing its public API.

src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/SelectOption.php

StaticSelectElement.phpMove static select element +0/-0

Move static select element

• Relocates the static select builder into the optional package.

src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/StaticSelectElement.php

UsersSelectElement.phpMove users select element +0/-0

Move users select element

• Relocates the users select builder into the optional package.

src/slack-notification-channel/src/Slack/BlockKit/Elements/Selects/UsersSelectElement.php

GeneratesDefaultIds.phpMove Block Kit ID-generation trait +0/-0

Move Block Kit ID-generation trait

• Keeps ID generation alongside the extracted elements that use it.

src/slack-notification-channel/src/Slack/BlockKit/Elements/Traits/GeneratesDefaultIds.php

AccessoryContract.phpMove Slack accessory contract +0/-0

Move Slack accessory contract

• Keeps the existing contract namespace while placing it beside the extracted builders.

src/slack-notification-channel/src/Slack/Contracts/AccessoryContract.php

BlockContract.phpMove Slack block contract +0/-0

Move Slack block contract

• Keeps the existing contract namespace while placing it beside the extracted blocks.

src/slack-notification-channel/src/Slack/Contracts/BlockContract.php

ElementContract.phpMove Slack element contract +0/-0

Move Slack element contract

• Keeps the existing contract namespace while placing it beside the extracted elements.

src/slack-notification-channel/src/Slack/Contracts/ElementContract.php

ObjectContract.phpMove Slack object contract +0/-0

Move Slack object contract

• Keeps the existing contract namespace within the optional package.

src/slack-notification-channel/src/Slack/Contracts/ObjectContract.php

EventMetadata.phpMove Slack event metadata +0/-0

Move Slack event metadata

• Relocates message metadata beside the Slack API message builder.

src/slack-notification-channel/src/Slack/EventMetadata.php

SlackMessage.phpMove Slack API message builder +0/-0

Move Slack API message builder

• Relocates the existing Block Kit message builder without changing its public namespace.

src/slack-notification-channel/src/Slack/SlackMessage.php

SlackRoute.phpMove Slack route value object +0/-0

Move Slack route value object

• Relocates route and token selection beside the extracted API channel.

src/slack-notification-channel/src/Slack/SlackRoute.php

SlackNotificationRouterChannel.phpRoute Slack deliveries to API or webhook channels +9/-7

Route Slack deliveries to API or webhook channels

• Moves the router to the package root, retains route-based transport selection, and accommodates both response types.

src/slack-notification-channel/src/SlackNotificationRouterChannel.php

Tests (31) +1764 / -146
LongWaitDetectedTest.phpTest Horizon’s default webhook channel +15/-0

Test Horizon’s default webhook channel

• Verifies that a Horizon webhook notification without a channel omits the override while retaining its legacy attachment payload.

tests/Horizon/Notifications/LongWaitDetectedTest.php

PackageMetadataTest.phpVerify Horizon declares the Slack dependency +1/-0

Verify Horizon declares the Slack dependency

• Checks that Horizon’s runtime requirements include the extracted package.

tests/Horizon/PackageMetadataTest.php

NotificationTransportModelTest.phpVerify queued transports coexist with model restoration +98/-0

Verify queued transports coexist with model restoration

• Uses a real serializing listener queue and SQLite recipient to verify restored database state and transport APIs for all three events.

tests/Integration/Notifications/Database/Sqlite/NotificationTransportModelTest.php

LegacySerializableTransportException.phpFixture for native legacy exception serialization +30/-0

Fixture for native legacy exception serialization

• Provides a Serializable exception to verify that custom PHP serialization remains authoritative.

tests/Notifications/Fixtures/LegacySerializableTransportException.php

NotificationChannelManagerTest.phpVerify Slack is no longer a core driver +6/-3

Verify Slack is no longer a core driver

• Checks that an unextended ChannelManager rejects Slack when the optional provider is absent.

tests/Notifications/NotificationChannelManagerTest.php

NotificationTransportSerializationTest.phpExercise queued transport snapshot boundaries +516/-0

Exercise queued transport snapshot boundaries

• Tests response and exception APIs, shared references, stream positions, metadata, custom hooks, trace arguments, and unsupported bodies through serialized listener payloads.

tests/Notifications/NotificationTransportSerializationTest.php

PackageMetadataTest.phpVerify core’s revised direct dependencies +11/-2

Verify core’s revised direct dependencies

• Checks transport snapshot requirements and confirms mbstring is no longer required by core Notifications.

tests/Notifications/PackageMetadataTest.php

NotificationSlackChannelTest.phpCover webhook payload edge cases +83/-2

Cover webhook payload edge cases

• Moves webhook coverage into the Slack suite and tests explicit unfurl values, zero-like attachment fields, channel clearing, and Guzzle request options.

tests/SlackNotificationChannel/NotificationSlackChannelTest.php

NotificationTransportQueueTest.phpTest both Slack transports through real queued listeners +319/-0

Test both Slack transports through real queued listeners

• Verifies delivered, sent, and failed events; original synchronous exceptions; closed bodies; declined queueing; and existing mail-failure handling.

tests/SlackNotificationChannel/NotificationTransportQueueTest.php

PackageMetadataTest.phpVerify standalone Slack package metadata +54/-0

Verify standalone Slack package metadata

• Checks dependencies, shared namespace autoloading, and provider discovery.

tests/SlackNotificationChannel/PackageMetadataTest.php

SlackChannelTest.phpTest the named-connection Slack API channel +296/-0

Test the named-connection Slack API channel

• Covers HTTP fakes, defaults and overrides, per-delivery token isolation, failures, and route and configuration precedence.

tests/SlackNotificationChannel/Slack/Feature/SlackChannelTest.php

SlackMessageTest.phpAdapt Slack message features to HTTP-client testing +123/-116

Adapt Slack message features to HTTP-client testing

• Moves feature tests to the extracted package layout and asserts requests after delivery through the new HTTP fake.

tests/SlackNotificationChannel/Slack/Feature/SlackMessageTest.php

SlackChannelTestNotifiable.phpMove the Slack notifiable fixture +1/-1

Move the Slack notifiable fixture

• Updates its test namespace for the extracted Slack suite.

tests/SlackNotificationChannel/Slack/Fixtures/SlackChannelTestNotifiable.php

SlackChannelTestNotification.phpMove the Slack notification fixture +1/-1

Move the Slack notification fixture

• Updates its test namespace for the extracted Slack suite.

tests/SlackNotificationChannel/Slack/Fixtures/SlackChannelTestNotification.php

TestCase.phpProvide a shared HTTP-fake Slack test harness +70/-0

Provide a shared HTTP-fake Slack test harness

• Registers the optional provider, fakes Slack API requests, and supplies payload and token assertions.

tests/SlackNotificationChannel/Slack/TestCase.php

ActionsBlockTest.phpTest zero-like action block IDs +10/-1

Test zero-like action block IDs

• Moves the test into the package unit layout and verifies an ID of 0 survives payload generation.

tests/SlackNotificationChannel/Slack/Unit/Blocks/ActionsBlockTest.php

ContextBlockTest.phpTest zero-like context block IDs +10/-1

Test zero-like context block IDs

• Moves the test into the package unit layout and adds zero-like ID coverage.

tests/SlackNotificationChannel/Slack/Unit/Blocks/ContextBlockTest.php

DividerBlockTest.phpTest zero-like divider IDs +6/-1

Test zero-like divider IDs

• Moves the test into the package unit layout and adds zero-like ID coverage.

tests/SlackNotificationChannel/Slack/Unit/Blocks/DividerBlockTest.php

HeaderBlockTest.phpTest zero-like header IDs +6/-1

Test zero-like header IDs

• Moves the test into the package unit layout and adds zero-like ID coverage.

tests/SlackNotificationChannel/Slack/Unit/Blocks/HeaderBlockTest.php

ImageBlockTest.phpTest zero-like image block IDs +8/-1

Test zero-like image block IDs

• Moves the test into the package unit layout and adds zero-like ID coverage.

tests/SlackNotificationChannel/Slack/Unit/Blocks/ImageBlockTest.php

SectionBlockTest.phpTest section ID and empty-field filtering +11/-1

Test section ID and empty-field filtering

• Verifies a zero-like ID remains present while an empty optional fields array stays omitted.

tests/SlackNotificationChannel/Slack/Unit/Blocks/SectionBlockTest.php

ConfirmObjectTest.phpMove confirmation composite tests +1/-1

Move confirmation composite tests

• Retains existing validation and payload coverage under the extracted package’s unit layout.

tests/SlackNotificationChannel/Slack/Unit/Composites/ConfirmObjectTest.php

PlainTextOnlyTextObjectTest.phpMove plain-text-only composite tests +1/-1

Move plain-text-only composite tests

• Retains existing plain-text coverage under the extracted package’s unit layout.

tests/SlackNotificationChannel/Slack/Unit/Composites/PlainTextOnlyTextObjectTest.php

TextObjectTest.phpMove text composite tests +1/-1

Move text composite tests

• Retains existing text-object coverage under the extracted package’s unit layout.

tests/SlackNotificationChannel/Slack/Unit/Composites/TextObjectTest.php

ButtonElementTest.phpTest zero-like button option values +9/-1

Test zero-like button option values

• Moves the tests into the package layout and verifies explicit zero-like values and accessibility labels remain in payloads.

tests/SlackNotificationChannel/Slack/Unit/Elements/ButtonElementTest.php

ImageElementTest.phpMove image element tests +1/-1

Move image element tests

• Retains existing image-element coverage in the package unit layout.

tests/SlackNotificationChannel/Slack/Unit/Elements/ImageElementTest.php

SelectOptionTest.phpMove select option tests +1/-1

Move select option tests

• Retains existing select-option coverage in the package unit layout.

tests/SlackNotificationChannel/Slack/Unit/Elements/Selects/SelectOptionTest.php

StaticSelectElementTest.phpMove static select tests +1/-1

Move static select tests

• Retains existing static-select coverage in the package unit layout.

tests/SlackNotificationChannel/Slack/Unit/Elements/Selects/StaticSelectElementTest.php

UsersSelectElementTest.phpMove users select tests +1/-1

Move users select tests

• Retains existing users-select coverage in the package unit layout.

tests/SlackNotificationChannel/Slack/Unit/Elements/Selects/UsersSelectElementTest.php

SlackChannelServiceProviderTest.phpVerify optional provider registration order +59/-0

Verify optional provider registration order

• Checks Slack driver registration whether the manager resolves before or after the provider, plus named connection registration.

tests/SlackNotificationChannel/SlackChannelServiceProviderTest.php

SlackNotificationRouterChannelTest.phpAdapt router tests to extracted channel types +14/-7

Adapt router tests to extracted channel types

• Tests the relocated router’s API response type and preserved route-based channel selection.

tests/SlackNotificationChannel/SlackNotificationRouterChannelTest.php

Documentation (4) +64 / -10
notifications.mdDocument Slack installation and queued event constraints +34/-1

Document Slack installation and queued event constraints

• Explains installation, named HTTP connection customization, restored queued listener APIs, and the synchronous fallback for unsupported bodies.

src/docs/notifications.md

README.mdRemove Slack-specific notes from core README +1/-9

Remove Slack-specific notes from core README

• Leaves core Notifications attributed to its framework source after Slack-specific code moves out.

src/notifications/README.md

LICENSE.mdLicense the extracted Slack package +23/-0

License the extracted Slack package

• Adds the package’s MIT license and attribution.

src/slack-notification-channel/LICENSE.md

README.mdIdentify the extracted Slack package +6/-0

Identify the extracted Slack package

• Provides the documentation link and upstream source attribution.

src/slack-notification-channel/README.md

Other (4) +67 / -2
composer.jsonWire the optional Slack package into the monorepo +6/-1

Wire the optional Slack package into the monorepo

• Adds the package to root requirements, shares the Notifications autoload prefix across both packages, and lists the Slack provider for monorepo discovery.

composer.json

composer.jsonDeclare Horizon’s Slack package requirement +1/-0

Declare Horizon’s Slack package requirement

• Adds the extracted channel package as a direct dependency for Horizon notifications.

src/horizon/composer.json

composer.jsonDeclare transport serialization dependencies +3/-1

Declare transport serialization dependencies

• Removes Slack’s mbstring requirement from core and declares PSR-7, HTTP client, and serializable-closure dependencies used by queued transport snapshots.

src/notifications/composer.json

composer.jsonDefine the independently installable Slack package +57/-0

Define the independently installable Slack package

• Declares its runtime requirements, preserved Notifications namespace, and provider-discovery metadata.

src/slack-notification-channel/composer.json

Comment thread src/slack-notification-channel/src/Channels/SlackWebhookChannel.php
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Malformed Slack replies look delivered 🐞 Bug ≡ Correctness
Description
SlackChannel::send() rejects only an explicit JSON ok: false, so a successful HTTP response with
no ok field or invalid JSON is returned as a successful delivery. When Slack or an intermediary
returns such a response, the notification reaches the delivered and sent events even though the
reply never confirms that Slack accepted the message.
Code

src/slack-notification-channel/src/Slack/SlackChannel.php[R56-57]

+        if ($response->successful() && $response->json('ok') === false) {
+            throw new RuntimeException('Slack API call failed with error [' . $response->json('error') . '].');
Evidence
The new condition accepts anything other than literal false, and the added test explicitly treats
an empty JSON object and non-JSON text as successes. The notification sender dispatches delivered
and sent events after the channel returns; the removed condition rejected HTTP 200 responses whose
ok value was absent.

src/slack-notification-channel/src/Slack/SlackChannel.php[50-60]
tests/SlackNotificationChannel/Slack/Feature/SlackChannelTest.php[124-145]
src/notifications/src/NotificationSender.php[166-180]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SlackChannel::send()` accepts HTTP-success responses with missing or invalid Slack success data, allowing unconfirmed notifications to be reported as delivered.
## Fix Focus Areas
- src/slack-notification-channel/src/Slack/SlackChannel.php[50-60]
- tests/SlackNotificationChannel/Slack/Feature/SlackChannelTest.php[124-145]
## Recommended Fix
Require the decoded `ok` value to be `true` before returning a successful Slack API response. Throw an informative exception for missing or invalid success data, and update the tests to cover those replies as failures.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Slack API errors without a code show an empty reason 🐞 Bug ◔ Observability
Description
SlackChannel::send now builds its failure message only from $response->json('error'). The old
code fell back to the raw response body ($result['error'] ?? $content), but that fallback was
removed. If Slack returns a 2xx response with ok: false and no error key, the exception reads
Slack API call failed with error []. and the response body that explains the failure is no longer
in the exception.
Code

src/slack-notification-channel/src/Slack/SlackChannel.php[R56-57]

+        if ($response->successful() && $response->json('ok') === false) {
+            throw new RuntimeException('Slack API call failed with error [' . $response->json('error') . '].');
Evidence
The removed lines 59-61 used ($result['error'] ?? $content), so the raw body was shown whenever
error was missing. The added line 57 joins $response->json('error') straight into the message.
That value is null when the key is absent, so the brackets end up empty.

src/slack-notification-channel/src/Slack/SlackChannel.php[50-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When Slack returns `ok: false` without an `error` key, the RuntimeException message no longer includes any detail. The old code fell back to the raw response body.
## Fix Focus Areas
- src/slack-notification-channel/src/Slack/SlackChannel.php[56-58]
## Recommended Fix
Build the message with `$response->json('error') ?? $response->body()` so the previous fallback to the raw body is restored.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/slack-notification-channel/src/Slack/SlackChannel.php
Comment thread src/slack-notification-channel/src/Slack/SlackChannel.php
@binaryfire
binaryfire merged commit e3a92ef into 0.4 Oct 6, 2026
55 checks passed
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.

1 participant