Skip to main content

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
# @doctest id="9f9e"
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
# @doctest id="3a0a"
What is built in:
  • the raw event stream stays on CanHandleEvents
  • the package projects stable RuntimeProgressUpdate objects 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 RuntimeProgressUpdate into 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
# @doctest id="97ee"
What is built in:
  • ExecuteAgentCtrlPromptMessage for queued AgentCtrl execution
  • ExecuteNativeAgentPromptMessage for queued native-agent execution and optional session resume
  • RuntimeObservationMessage for 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
# @doctest id="1620"
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.