Symfony Operations Guide¶
This guide explains how the current Symfony package behaves in the runtime shapes that matter most in production:
- web controllers and page requests
- JSON APIs and streaming endpoints
- Messenger workers
- CLI commands
The package already ships one coherent operational baseline. What changes by app shape is which optional delivery layer you put on top of that baseline.
Built-In Baseline¶
The built-in operational surface is:
- one package-owned runtime bus at
Cognesy\Events\Contracts\CanHandleEvents - optional Symfony EventDispatcher mirroring controlled by
instructor.events.dispatch_to_symfony - optional projected progress updates at
Cognesy\Instructor\Symfony\Delivery\Progress\Contracts\CanHandleProgressUpdates - optional Messenger observation forwarding under
instructor.delivery.messenger - optional CLI observation output under
instructor.delivery.cli - package-owned telemetry exporter selection, projector composition, and lifecycle cleanup
- package-owned logging presets attached to the same runtime bus
That means:
- logging and telemetry are built in once enabled
- Symfony listener mirroring is optional
- Messenger observation forwarding is optional
- SSE, WebSockets, Mercure, and polling layers are still application-owned
Web Controllers¶
Typical shape:
- synchronous request or form handling
- normal Symfony controller lifecycle
- request-scoped route and request metadata available for enrichment
Recommended package setup:
instructor:
events:
dispatch_to_symfony: true
logging:
enabled: true
preset: development
telemetry:
enabled: true
driver: otel
What is built in:
- the runtime bus remains the source of truth
- Symfony listeners can observe mirrored lifecycle events
- telemetry flushes on kernel terminate
- logging enrichers can use request-aware framework data when available
What stays application-specific:
- controller-specific domain listeners
- UI notifications
- any transport from runtime progress into browser-visible updates
JSON APIs And Streaming Endpoints¶
Typical shape:
- HTTP request/response flow
- API monitoring and correlation matter more than controller-local side effects
- partial progress may need to feed SSE, WebSockets, Mercure, or polling
Recommended package setup:
instructor:
telemetry:
enabled: true
driver: otel
logging:
enabled: true
preset: production
delivery:
progress:
enabled: true
What is built in:
- the raw event stream stays on
CanHandleEvents - the package projects stable
RuntimeProgressUpdateobjects onto the progress bus - telemetry and logging stay attached to the internal bus instead of depending on Symfony listeners
What stays application-specific:
- SSE response formatting
- WebSocket or Mercure publication
- API-specific mapping from
RuntimeProgressUpdateinto client-visible payloads
The important rule is:
build transport-specific streaming on top of RuntimeProgressUpdate, not on assumptions that every raw runtime event should become a public API payload.
Messenger Workers¶
Typical shape:
- queued execution
- background follow-up work
- no active
RequestStack
Recommended package setup:
instructor:
delivery:
messenger:
enabled: true
bus_service: message_bus
observe_events:
- Cognesy\Agents\Events\AgentExecutionCompleted
- Cognesy\AgentCtrl\Event\AgentExecutionCompleted
telemetry:
enabled: true
driver: otel
logging:
enabled: true
preset: production
What is built in:
ExecuteAgentCtrlPromptMessagefor queued AgentCtrl executionExecuteNativeAgentPromptMessagefor queued native-agent execution and optional session resumeRuntimeObservationMessagefor explicit observation forwarding- telemetry flush and shutdown hooks around handled, failed, and stopped worker lifecycle events
What stays application-specific:
- which runtime events should be forwarded onto Messenger
- worker-specific retry, dead-letter, or alerting policy
- downstream consumers of forwarded observation messages
Keep Messenger observation explicit.
Use observe_events as an allow-list instead of treating Messenger as a generic mirror for the entire raw runtime stream.
CLI Commands¶
Typical shape:
- console command or long-running CLI workflow
- no HTTP request context
- human-readable live observation can be useful
Recommended package setup:
instructor:
delivery:
cli:
enabled: true
use_colors: true
show_timestamps: true
telemetry:
enabled: true
driver: otel
logging:
enabled: true
preset: development
What is built in:
- the same runtime bus semantics as HTTP and Messenger flows
- projected progress updates formatted by
SymfonyCliObservationPrinter - telemetry flush and shutdown around console terminate and error events
What stays application-specific:
- command-specific output structure
- custom progress formatting beyond the built-in printer
- command orchestration and retries
CLI output is opt-in for a reason: the package keeps runtime correctness on the internal bus and treats terminal formatting as a view over projected progress, not as a second event model.
Built-In Versus Optional¶
Built in once enabled by config:
- logging presets
- telemetry exporter selection
- telemetry lifecycle cleanup
- progress projection
- Messenger execution handlers
- Messenger observation bridge
- CLI progress printer
Still application-owned:
- browser delivery transports
- SSE and WebSocket response format
- Mercure integration
- alert routing and incident handling
- domain-specific listeners and notifications
Operational Rule Of Thumb¶
Use the package-owned runtime bus for correctness and observability. Use the projected progress bus for transport-specific UX. Use Symfony listeners and Messenger forwarding only where they improve application integration, not as the primary source of runtime truth.