Context
Polling behavior in OneCMS exists across multiple patterns with no shared infrastructure contract.
Current polling logic is primarily embedded in LiveStreamRuntime, with additional timer-driven behavior in feature/UI layers. This creates repeated problems:
- No process-wide deduplication by logical polling key
- No shared ref-counted lifecycle across multiple subscribers
- Inconsistent pause/resume behavior, especially for hidden-tab scenarios
- Repeated reimplementation of scheduling concerns in domain code
- Increased risk of duplicate requests when multiple surfaces observe the same entity
At the same time, live stream state, encoding progress, chapter sync, and future job tracking all require the same scheduling and lifecycle primitives, while preserving domain ownership of stop/terminal rules.
A decision was needed on whether polling should remain ad-hoc per module or move to a centralized internal infrastructure primitive.
Decision
Adopt a centralized internal PollingManager under src/lib/internal/PollingManager/ as the single polling infrastructure layer.
PollingManager owns:
- key-based task registry
- deduplicated scheduling (one task per key)
- ref-counted subscription lifecycle
- in-flight overlap protection
- global/per-key pause and resume
- optional visibility-aware behavior through explicit
documentRefinjection
Domain modules (for example LiveStreamRuntime) remain responsible for domain semantics (pollFn, shouldStop, key composition, result interpretation), and delegate scheduling/lifecycle mechanics to PollingManager.
The runtime contract is singleton-first for application usage, while preserving class export for isolated tests.

Consequences
Enables
- Shared deduplication across list/detail and other concurrent consumers
- Deterministic polling lifecycle and cleanup behavior
- Consistent pause/resume and hidden-tab handling policy
- Cleaner separation between domain logic and infra scheduling mechanics
- Reusable foundation for additional polling consumers without repeating timer plumbing
Restricts
- New polling implementations should not introduce direct ad-hoc timer loops in feature code when
PollingManagercan be used - Polling consumers must define stable keys and follow manager lifecycle rules
- Polling API surface is intentionally constrained to avoid multiple overlapping unsubscribe/update pathways
Trade-offs Accepted
- Added internal infrastructure surface area and test responsibility
- Initial migration effort to adapt existing runtime pollers to centralized mechanics
- Centralized behavior requires stricter API discipline up front (immutability of task-defining fields)
Notes
- This ADR does not supersede ADR-021; it complements it.
- ADR-021 keeps live-stream domain orchestration in
LiveStreamRuntime. - ADR-029 centralizes polling scheduling and lifecycle infrastructure.
- ADR-021 keeps live-stream domain orchestration in
- For same-key subscriptions, later subscribers attach to the existing task (no extra immediate poll), and visibility pause remains authoritative while document is hidden.
- Detailed implementation contract lives in
.idea/polling-manager.spec.md.