Skip to content

simplify(streams): make operation-event lifetime belong to its daemon instead of a process-global singleton #4450

Open

Description

@ScriptedAlchemy

Priority / deletion target

P2 · lifecycle/ownership consolidation · medium integration and shutdown risk. Preserve canonical operation streams, managed test-run progress, authenticated resume and durable effect receipts. Remove ambient process-global event ownership and the cross-composition cleanup rules it requires.

Evidence

Audited at 1bf9f0819465b2720241b20057510ff01dfa633a:

  • operation_stream.rs:1–141 describes a daemon-owned, intentionally memory-only authority, but constructs it in static OPERATION_EVENTS: OnceLock<OperationEventAuthority>. Every caller of operation_event_authority() receives that same process owner.
  • expire_idle, lines 840–862 explicitly explains that all daemon compositions and some managed-test handlers share the singleton. To avoid one composition truncating another's streams, shutdown scans the shared map and uses cancellation-channel receiver counts as a producer-liveness proxy.
  • Production call sites include tracedecay-daemon-service/src/invocation.rs initializing operation_events from the global accessor, its forwarding accessor in invocation/feedback.rs, tracedecay/src/daemon/http_application_router.rs, project_open_owners/advisory_runtime.rs, MCP handlers/workflow.rs and tracedecay-application/src/lsp_runtime/managed_test_runs.rs. Producer, HTTP subscriber and LSP projection obtain the same authority through ambient lookup rather than through the owning composition.
  • The existing authority already has bounded events/operations/subscribers and typed generation/resume identities; these should remain the canonical implementation rather than be replaced.

The source documents the cross-composition coupling. This issue does not allege an authorization bypass or claim normal deployments run several profiles in one process. The simplification is explicit lifetime ownership, with independently constructed compositions/tests no longer sharing admission/cleanup state by accident.

Exact implementation

  1. Construct OperationEventAuthority in the real daemon composition/session lifetime and pass its existing cloneable handle to producers, HTTP/SSE routes, managed-test readers and LSP projection. Use the field already carried in daemon invocation state; do not introduce a service locator, new trait hierarchy or static map keyed by daemon ID.
  2. Give supported standalone managed-test execution an explicit owner at its entry point and pass that same handle to its observers. Do not create a fresh authority on each accessor call: producer and subscriber must share the exact intended owner, and supported cross-surface visibility must remain intact.
  3. Delete OPERATION_EVENTS, the global accessor, forwarding-only daemon_operation_event_authority() and ambient lookup sites after migration. Resolve any dependency cycle by passing the existing handle through the narrow constructor boundary, not by moving the singleton elsewhere.
  4. Replace global cross-composition expire_idle sweeping with shutdown of only this owner's streams, after its accepted work has reached the existing safe settlement point. Preserve needed producer liveness during that owner's drain; remove only the receiver-count workaround whose purpose is shielding unrelated compositions. Do not equate a disconnected observer with cancellation of an accepted effect.
  5. Retain bounded replay/live semantics, exact actor/scope/disclosure checks, generation-bound resume tokens, terminal receipts and explicit cancellation. Durable journals remain in their present stores; do not persist this memory-only stream or add a second durable event log.
  6. Bind new daemon incarnations to new resume authority and report old frontiers expired. Retire tests that reset or serialize around a shared singleton; exercise independent real compositions with controlled producers instead.

Acceptance

Two independent compositions can reach their own configured operation limits without sharing a capacity pool; shutting one down does not close the other's stream or remove its history. Within one composition, MCP producer, HTTP subscriber and LSP projection see the same managed run. A completed/abandoned old owner cannot publish into its successor. Disconnect does not cancel accepted work; explicit authorized cancel still works and durable settlement survives owner shutdown. Restart invalidates only old memory frontiers with the documented typed response. Run operation-stream, managed-test, HTTP/MCP/LSP and daemon shutdown tests through Bazel with nonzero counts.

Coordinate with #4432, which should reuse this canonical stream rather than add another Explorer job owner. Preserve #4405's producer-watermark versus delivery-cursor correction; this ticket changes owner construction, not sequence meaning. Targeted OPERATION_EVENTS issue search returned no existing task. Static source/caller tracing only; no Rust integration run or production multi-daemon failure was reproduced while filing.

Activity

  1. ScriptedAlchemy commented on Nov 10, 2026

    @ScriptedAlchemy
    OwnerAuthor

    2026-11-10 UTC sweep: existing non-model PR #4850 has a new verified one-parent, guarded non-force head e18430ebd3987247d8354c108d844d4e70ffaadb (commit). Exact prior-head CI 42718580170 executed 183 Linux Bazel targets: 182 passed, one hooks_lsp_suite fixture failed because 65 Stop callbacks left one independent pending_work obligation backpressured even after all delivery receipts settled. The receipt batch regression now uses 65 validated distinct real-domain delivery receipts via the production spool writer and daemon settlement, preserving exact 65 persisted-file, 10-second drain, and status=drained assertions; unrelated native callback tests remain. No source or lifecycle/backoff limits changed. Exact-head CI 42734412375 is QUEUED, not proven. Keep Refs #4450, PR OPEN UNMERGED, issue OPEN until HTTP/MCP/LSP/advisory/shutdown, two-daemon isolation, restart/resume/cancellation and reviewed CI evidence are complete. Protected decision-model PRs, releases, schedules and task owners unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions