re #21 add univeros/messaging — Symfony Messenger bridge#75
Merged
Conversation
added 4 commits
May 27, 2026 06:31
Wraps Symfony Messenger behind framework-owned MessageBusInterface, discovers handlers via #[AsHandler], wires transports from env, and ships worker/retry/show CLI commands. Scaffolder gains a queue: block.
Builds on the WIP messaging scaffold: - bin/altair includes src/Altair/Messaging/Cli so worker / worker:retry-failed / worker:show-failed commands are picked up out of the box. - Scaffold gains a queue: block: Parser/Validator parse and check it, three new emitters (MessageEmitter, HandlerEmitter, HandlerTestEmitter) produce a readonly DTO + #[AsHandler] handler stub + golden PHPUnit test, and EmissionPlan wires them. - MessengerConfiguration breaks the bus/transport construction cycle via a LazyBus so SyncTransportFactory can be registered safely. - Adds the messaging package manifest under .agent/packages/messaging.md and the human-facing guide at docs/packages/messaging.md; updates docs/README, AGENT.md (sub-package layout) and CLAUDE.md (messaging section) accordingly. - Tests: 30 messaging tests (attribute, registry, discoverer, handler locator, message bus, transport settings, middleware, configuration wiring, sync + async InMemory integration) plus three scaffold queue emitter tests and parser/validator coverage. composer qa green: cs clean, phpstan no errors, 86/86 tests pass in the scaffold + messaging suites.
Caught while running the PR test plan: `bin/altair worker` invoked straight from the framework checkout exits with "TransportSettings is not instantiable" because the framework's bin/altair only wires CLI discovery (CliConfiguration), not domain configurations. The same is true of `bin/altair db:migrate`. Documents the constraint and shows a host entry-point that applies MessengerConfiguration before running the Application.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #21.
Summary
Adds the
univeros/messagingsub-package — a thinMessageBus+ worker bridge over Symfony Messenger, wired through the framework's container, with attribute-driven handler discovery. Extends the scaffolder with aqueue:block so a YAML spec emits a message DTO + handler stub + handler test alongside the HTTP artifacts.What's in the package
MessageBusInterface(dispatch(object, Stamp[]): Envelope) and a markerHandlerInterface. Application code only ever imports the framework contract, neverSymfony\Component\Messenger\*.#[AsHandler(MessageClass::class)]on any class with an__invokemethod is enough — nogetHandledMessages()ceremony.AttributeHandlerDiscovererscanshandlerPathsat boot (mirroringAltair\Cli\Discovery\AttributeCommandDiscoverer's tokenizer-first approach so non-handler files are never autoloaded).MESSENGER_TRANSPORT_DEFAULT=…,MESSENGER_TRANSPORT_HIGH=…,MESSENGER_FAILURE_TRANSPORT=…,MESSENGER_ROUTING="App\Msg\Foo:default,App\Msg\Bar:default|high". Sync + InMemory always available; Redis / Doctrine / AMQP / Beanstalkd detected reflectively if their bridge packages are installed.bin/altair worker [--transports=…] [--time-limit=N] [--memory-limit=128M] [--limit=N]with PCNTL SIGTERM/SIGINT for graceful drain.bin/altair worker:show-failedlists envelopes in the failure transport;worker:retry-failed --limit=Nstrips theSentToFailureTransportStampand re-dispatches.queue:block in a spec emitsapp/Messages/<Name>.php(readonly DTO),app/Messages/<Name>Handler.php(#[AsHandler]-decorated stub), andtests/Messages/<Name>HandlerTest.php(golden test) in the samebin/altair spec:scaffoldpass that already produces the HTTP artifacts.The wrap exists for vendor neutrality, a consistent DI story (handlers resolved through
Altair\Container), and a single source of truth for the wire format + the async contract via the spec.Architecture notes
MessageBuswith two middlewares —SendMessageMiddleware(routing-aware) thenContainerHandlerMiddleware(a tagged subtype ofHandleMessageMiddlewarewith ourHandlerLocator).HandlerLocatorresolves handler instances throughAltair\Containerand filters byfromTransport/ priority before yieldingHandlerDescriptors.SyncTransportFactory→ bus construction cycle is broken viaLazyBus, which resolves the bus at firstdispatch()time. This lets sync routing coexist with async transports without manual ordering.Host-application boot is required
The framework's
bin/altaironly wires CLI discovery (CliConfiguration); it does not applyMessengerConfiguration. Invokingbin/altair workerfrom a fresh framework checkout fails withTransportSettings is not instantiable— the same caveat that applies tobin/altair db:migrate(which needsCycleOrmConfigurationapplied first). The host application is expected to ship its own entry script that applies the configurations it uses, then hands off toAltair\Cli\Application::run(). Seedocs/packages/messaging.md#running-workersfor the boot snippet.Tests
tests/Messaging/: attribute reading, registry priority/transport filtering, discoverer scan, handler locator, message bus wrap, transport settings env parsing, configuration container wiring, sync dispatch, async InMemory → worker → handle integration, and the optionalLoggingMiddleware.queue:block: parser, validator (positive + negative cases), and the three emitters.composer csclean.composer stanno errors.vendor/bin/phpunit tests/Messaging tests/Scaffold tests/Cli: 133 / 0 / 21 (tests / failures / pre-existing deprecations inAltair\Container). Full suite has 6 pre-existing environmental failures (MongoDB / PDO session tests that need infrastructure) — none introduced by this change.Test plan (verified locally)
composer installafter pulling the branch — cleancomposer csclean,composer stanno errorsvendor/bin/phpunit tests/Messaging tests/Scaffold tests/Cli— 133 tests passbin/altair listshowsworker,worker:retry-failed,worker:show-failed;bin/altair worker --helpprints expected usagequeue:block, runbin/altair spec:scaffold path/to/foo.yaml --dry-run—app/Messages/SendWelcomeEmail.php,app/Messages/SendWelcomeEmailHandler.php, andtests/Messages/SendWelcomeEmailHandlerTest.phpare listed alongside the HTTP artifactsbin/altair manifest:generate --checkexits 0MessengerConfigurationin an app entry script, thenMESSENGER_TRANSPORT_DEFAULT=in-memory:// bin/altair worker --limit=1consumes from the in-memory transport. Covered bytests/Messaging/Integration/DispatchAndConsumeTest.php.Caveat for reviewers
composer qareports 6 pre-existing test errors (5 MongoSessionHandler, 1 PdoSessionHandler) and 67 deprecation notices fromAltair\Containerconstructors that pre-date this branch. CS and PHPStan are clean.