docs
This commit is contained in:
589
docs/subsystems/ControlServices.md
Normal file
589
docs/subsystems/ControlServices.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# ControlServices Subsystem Design
|
||||
|
||||
This document expands the `ControlServices` subsystem described in [PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md). It defines the target role of `ControlServices` as the ingress boundary for non-render control sources and the normalization layer that turns external input into typed internal actions.
|
||||
|
||||
The intent here is to make `ControlServices` explicit enough that later phases can extract it from the current `RuntimeServices` / `ControlServer` / `OscServer` mix without inventing new boundaries ad hoc.
|
||||
|
||||
## Purpose
|
||||
|
||||
`ControlServices` is the subsystem that accepts external control traffic and turns it into safe, typed, low-cost input for the rest of the app.
|
||||
|
||||
In the target architecture, `ControlServices` should:
|
||||
|
||||
- own ingress for OSC, HTTP/REST-style control routes, WebSocket session management, and file-watch/reload signals
|
||||
- normalize transport-specific payloads into typed internal actions or events
|
||||
- apply ingress-local buffering, coalescing, deduplication, and rate limiting where useful
|
||||
- expose service timing and health observations to `HealthTelemetry`
|
||||
- forward normalized actions into `RuntimeCoordinator`
|
||||
|
||||
It should not:
|
||||
|
||||
- decide persistence policy
|
||||
- mutate persisted state directly
|
||||
- build render snapshots
|
||||
- own render-local overlay state
|
||||
- own device timing or playout policy
|
||||
|
||||
This subsystem is intentionally narrow in authority and broad in transport coverage.
|
||||
|
||||
## Why This Subsystem Exists
|
||||
|
||||
Today the app already has a recognizable control-services slice, but it is spread across several classes:
|
||||
|
||||
- `RuntimeServices` hosts control server startup, OSC queues, deferred OSC commits, and file-watch polling:
|
||||
- [RuntimeServices.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/RuntimeServices.h:26)
|
||||
- [RuntimeServices.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/RuntimeServices.cpp:24)
|
||||
- `ControlServer` owns HTTP, WebSocket upgrade, static asset serving, and direct callback-based route dispatch:
|
||||
- [ControlServer.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/ControlServer.h:15)
|
||||
- [ControlServer.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/ControlServer.cpp:88)
|
||||
- `OscServer` owns UDP socket receive, OSC decode, and parameter callback dispatch:
|
||||
- [OscServer.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/OscServer.h:11)
|
||||
- [OscServer.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/OscServer.cpp:58)
|
||||
|
||||
The current shape works, but it mixes:
|
||||
|
||||
- transport handling
|
||||
- action normalization
|
||||
- direct callback dispatch
|
||||
- coarse background polling
|
||||
- transient queue ownership
|
||||
- UI broadcast behavior
|
||||
- partial runtime mutation coordination
|
||||
|
||||
That overlap is exactly what Phase 1 is trying to remove.
|
||||
|
||||
## Design Goals
|
||||
|
||||
`ControlServices` should optimize for:
|
||||
|
||||
- low-latency ingress without forcing immediate whole-app work
|
||||
- clear transport boundaries
|
||||
- deterministic normalization of external input
|
||||
- isolation of service-specific timing concerns
|
||||
- easy replacement of polling flows with typed events
|
||||
- no direct knowledge of render-local implementation details
|
||||
- safe behavior under bursty traffic such as high-rate OSC
|
||||
|
||||
## Subsystem Responsibilities
|
||||
|
||||
`ControlServices` owns the following concerns.
|
||||
|
||||
### 1. Transport Ingress
|
||||
|
||||
It accepts input from external control-facing sources such as:
|
||||
|
||||
- OSC/UDP parameter control
|
||||
- HTTP API requests from the native control UI or external clients
|
||||
- WebSocket connection lifecycle for state consumers
|
||||
- file-watch triggers and manual reload requests
|
||||
- future automation ingress such as MIDI, serial, or remote control bridges
|
||||
|
||||
The key rule is that transport-specific details stop here.
|
||||
|
||||
### 2. Action Normalization
|
||||
|
||||
Every ingress path should be converted into a typed internal action or event before it touches runtime policy.
|
||||
|
||||
Examples:
|
||||
|
||||
- OSC `/layer/param` traffic becomes `AutomationTargetReceived`
|
||||
- `POST /api/layers/add` becomes `LayerAddRequested`
|
||||
- `POST /api/reload` becomes `ShaderReloadRequested`
|
||||
- file-watch changes become `RegistryChangedDetected` or `ReloadRequested`
|
||||
|
||||
The rest of the app should not need to know whether an action came from UDP, HTTP, the embedded UI, or a background watcher.
|
||||
|
||||
### 3. Ingress-Local Buffering and Coalescing
|
||||
|
||||
`ControlServices` may maintain short-lived queues or coalesced maps when that is the correct place to absorb bursty input.
|
||||
|
||||
Examples:
|
||||
|
||||
- latest-value coalescing per OSC route
|
||||
- pending reload edge detection
|
||||
- bounded outbound state-broadcast requests
|
||||
- short-lived delivery queues for already-classified follow-up work, as long as commit and persistence policy still belong to `RuntimeCoordinator`
|
||||
|
||||
This state is ingress-local and must not become a substitute for committed runtime state.
|
||||
|
||||
### 4. WebSocket Session Management
|
||||
|
||||
The subsystem owns connection lifecycle for clients that observe runtime state, but it does not own the authoritative runtime model.
|
||||
|
||||
It is responsible for:
|
||||
|
||||
- accepting WebSocket upgrades
|
||||
- tracking connected clients
|
||||
- forwarding serialized state snapshots or health payloads produced elsewhere
|
||||
- applying broadcast throttling or collapse policies when necessary
|
||||
|
||||
It is not responsible for deciding what the authoritative state is.
|
||||
|
||||
### 5. File-Watch and Reload Ingress
|
||||
|
||||
The subsystem should own the detection side of registry/file changes and reload requests.
|
||||
|
||||
It may:
|
||||
|
||||
- observe filesystem changes
|
||||
- debounce bursts of related file events
|
||||
- translate those changes into typed reload actions
|
||||
|
||||
It should not directly trigger render rebuilds or mutate shader/package state itself.
|
||||
|
||||
### 6. Service Health and Timing Reporting
|
||||
|
||||
`ControlServices` should emit operational signals into `HealthTelemetry`, including:
|
||||
|
||||
- OSC packet rate
|
||||
- OSC decode failures
|
||||
- queue depth / coalesced route count
|
||||
- dropped or collapsed ingress events
|
||||
- HTTP error counts
|
||||
- WebSocket connection count
|
||||
- reload request frequency
|
||||
- file-watch failures
|
||||
- service-thread startup/shutdown errors
|
||||
|
||||
## Explicit Non-Responsibilities
|
||||
|
||||
The following must stay outside `ControlServices` in the target design.
|
||||
|
||||
### Persistence Decisions
|
||||
|
||||
The subsystem may report that an input requested a state change, but it should not decide whether that change is persisted.
|
||||
|
||||
That belongs to `RuntimeCoordinator` and `RuntimeStore`.
|
||||
|
||||
### Render Snapshot Publication
|
||||
|
||||
`ControlServices` must not publish render-facing snapshots or poke render-local structures directly.
|
||||
|
||||
### Render-Local Overlay Ownership
|
||||
|
||||
Live OSC overlays, temporal state, shader feedback, and render-only transient state belong to `RenderEngine`.
|
||||
|
||||
`ControlServices` may ingest automation targets, but it should not own how those targets are applied inside the render domain.
|
||||
|
||||
### Hardware Timing or Playout Recovery
|
||||
|
||||
Device scheduling, queue headroom, and callback recovery belong to `VideoBackend`, not the control ingress path.
|
||||
|
||||
## Ingress Boundary Model
|
||||
|
||||
The clean boundary for `ControlServices` is:
|
||||
|
||||
- external transport in
|
||||
- typed action/event out
|
||||
|
||||
That implies three layers inside the subsystem.
|
||||
|
||||
### Transport Adapters
|
||||
|
||||
These are protocol-facing components.
|
||||
|
||||
Examples:
|
||||
|
||||
- `OscIngress`
|
||||
- `HttpControlIngress`
|
||||
- `WebSocketSessionHost`
|
||||
- `FileWatchIngress`
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- socket/file watcher lifecycle
|
||||
- protocol decoding
|
||||
- request framing
|
||||
- transport-level validation
|
||||
- low-level authentication or origin checks later if added
|
||||
|
||||
### Normalization Layer
|
||||
|
||||
This layer translates decoded transport input into typed actions.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- route parsing
|
||||
- payload type normalization
|
||||
- parameter name/key resolution where that is purely syntactic
|
||||
- conversion from transport-specific errors into typed ingress errors
|
||||
|
||||
This layer should not perform deep runtime mutation policy.
|
||||
|
||||
### Service Coordination Shell
|
||||
|
||||
This shell owns:
|
||||
|
||||
- startup/shutdown ordering for ingress services
|
||||
- shared ingress-local queues
|
||||
- service-thread lifecycle
|
||||
- handing normalized actions to `RuntimeCoordinator`
|
||||
- handing outbound snapshot payloads to WebSocket clients
|
||||
|
||||
This shell is the spiritual successor to the hosting part of current `RuntimeServices`, but with a much narrower responsibility set.
|
||||
|
||||
## Service Timing Concerns
|
||||
|
||||
`ControlServices` is the correct place to isolate transport-level timing concerns that should not leak into whole-app state policy.
|
||||
|
||||
### OSC Timing
|
||||
|
||||
Current behavior already points in the right direction:
|
||||
|
||||
- OSC receive is on its own thread in `OscServer`
|
||||
- latest values are coalesced by route in `RuntimeServices`
|
||||
- updates are applied once per render tick rather than per packet
|
||||
|
||||
Relevant code:
|
||||
|
||||
- [OscServer.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/OscServer.cpp:95)
|
||||
- [RuntimeServices.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/RuntimeServices.cpp:65)
|
||||
- [RuntimeServices.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/RuntimeServices.cpp:82)
|
||||
|
||||
Target rule:
|
||||
|
||||
- network receive and decode stay inside `ControlServices`
|
||||
- coalescing policy stays inside `ControlServices`
|
||||
- classification of the resulting action belongs to `RuntimeCoordinator`
|
||||
- render-local application belongs to `RenderEngine`
|
||||
|
||||
This keeps high-rate ingress cheap without giving the service layer authority over render behavior or committed-state policy.
|
||||
|
||||
### HTTP / UI Timing
|
||||
|
||||
HTTP control requests are operator-facing and usually low-rate, but the UI can still generate bursts through slider drags or repeated parameter edits.
|
||||
|
||||
`ControlServices` should:
|
||||
|
||||
- normalize each request into a typed action
|
||||
- allow collapse/throttle policies for purely observational outbound state pushes
|
||||
- avoid synchronous full-state serialization on every ingress event where possible
|
||||
|
||||
It should not decide whether a request results in immediate, deferred, transient, or persisted mutation. That is a coordinator concern.
|
||||
|
||||
### WebSocket Broadcast Timing
|
||||
|
||||
Outbound state streaming is control-plane behavior, not core runtime ownership.
|
||||
|
||||
Current code already distinguishes immediate and requested broadcasts:
|
||||
|
||||
- [ControlServer.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/ControlServer.cpp:163)
|
||||
- [ControlServer.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/ControlServer.cpp:170)
|
||||
|
||||
Target rule:
|
||||
|
||||
- `ControlServices` may own broadcast scheduling and collapse policy
|
||||
- the source state payload should come from snapshot/telemetry producers, not from service-owned mutable state
|
||||
|
||||
### File-Watch Timing
|
||||
|
||||
Current file-watch and deferred OSC commit work run on a coarse poll loop:
|
||||
|
||||
- [RuntimeServices.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/RuntimeServices.cpp:194)
|
||||
|
||||
This is one of the cleaner migration opportunities in the whole app.
|
||||
|
||||
Target rule:
|
||||
|
||||
- file-watch detection belongs in `ControlServices`
|
||||
- coarse polling should eventually be replaced with either event-driven watching or a narrower, typed background loop
|
||||
- detected changes should be debounced and surfaced as typed reload-related actions
|
||||
|
||||
### Service Backpressure
|
||||
|
||||
`ControlServices` needs explicit backpressure rules for high-rate sources.
|
||||
|
||||
Recommended policies:
|
||||
|
||||
- coalesce latest-value automation by route
|
||||
- bound per-service queues
|
||||
- count and report dropped/coalesced events
|
||||
- prefer collapsing observation work before collapsing operator mutations
|
||||
- never let service queues become hidden durable state
|
||||
|
||||
## Interfaces
|
||||
|
||||
These are suggested target-facing interfaces, not final class signatures.
|
||||
|
||||
### Subsystem Shell
|
||||
|
||||
Possible top-level responsibilities:
|
||||
|
||||
- `Start(...)`
|
||||
- `Stop()`
|
||||
- `PublishStateSnapshot(...)`
|
||||
- `PublishHealthSnapshot(...)`
|
||||
- `DrainNormalizedActions(...)`
|
||||
|
||||
The shell should feel like a host for ingress adapters plus a normalization/buffering boundary.
|
||||
|
||||
### OSC Ingress
|
||||
|
||||
Possible responsibilities:
|
||||
|
||||
- `StartOscIngress(...)`
|
||||
- `StopOscIngress()`
|
||||
- `ConfigureOscBinding(...)`
|
||||
- `EnqueueDecodedOscMessage(...)`
|
||||
- `DrainCoalescedAutomationTargets(...)`
|
||||
|
||||
### HTTP / Web Control Ingress
|
||||
|
||||
Possible responsibilities:
|
||||
|
||||
- `StartHttpIngress(...)`
|
||||
- `StopHttpIngress()`
|
||||
- `HandleHttpRequest(...)`
|
||||
- `HandleWebSocketUpgrade(...)`
|
||||
- `QueueStateBroadcastRequest()`
|
||||
|
||||
### File-Watch Ingress
|
||||
|
||||
Possible responsibilities:
|
||||
|
||||
- `StartFileWatchIngress(...)`
|
||||
- `StopFileWatchIngress()`
|
||||
- `PollOrConsumeFileEvents(...)`
|
||||
- `DrainReloadSignals(...)`
|
||||
|
||||
### Normalized Action Types
|
||||
|
||||
These should likely become shared event/action definitions in Phase 2, but `ControlServices` should be designed around them now.
|
||||
|
||||
Examples:
|
||||
|
||||
- `LayerAddRequested`
|
||||
- `LayerRemovedRequested`
|
||||
- `LayerReorderedRequested`
|
||||
- `LayerBypassSetRequested`
|
||||
- `LayerShaderSetRequested`
|
||||
- `ParameterSetRequested`
|
||||
- `LayerResetRequested`
|
||||
- `StackPresetSaveRequested`
|
||||
- `StackPresetLoadRequested`
|
||||
- `ShaderReloadRequested`
|
||||
- `ScreenshotRequested`
|
||||
- `AutomationTargetReceived`
|
||||
- `RegistryChangeDetected`
|
||||
|
||||
## Data Ownership Inside The Subsystem
|
||||
|
||||
`ControlServices` is allowed to own ingress-local ephemeral state.
|
||||
|
||||
Examples:
|
||||
|
||||
- connected WebSocket client list
|
||||
- pending broadcast flag
|
||||
- coalesced OSC route map
|
||||
- outstanding decoded-but-undrained action queue
|
||||
- file-watch debounce state
|
||||
- transport error counters before publication to telemetry
|
||||
|
||||
It should not own:
|
||||
|
||||
- authoritative layer stack state
|
||||
- committed parameter values
|
||||
- render snapshots
|
||||
- playout queue state
|
||||
- shader feedback or render overlays
|
||||
|
||||
The rule is simple:
|
||||
|
||||
- if the state exists only to absorb or forward external input, it can live here
|
||||
- if the state defines how the app should behave over time, it belongs elsewhere
|
||||
|
||||
## Outbound Boundaries
|
||||
|
||||
`ControlServices` talks outward in only a few approved directions.
|
||||
|
||||
### To `RuntimeCoordinator`
|
||||
|
||||
Primary outbound path.
|
||||
|
||||
It sends:
|
||||
|
||||
- normalized mutation requests
|
||||
- automation targets
|
||||
- reload requests
|
||||
- stack preset requests
|
||||
|
||||
It does not send:
|
||||
|
||||
- transport-specific objects such as raw sockets or OSC packet structures
|
||||
- render-facing state objects
|
||||
|
||||
### To `HealthTelemetry`
|
||||
|
||||
Observation-only relationship.
|
||||
|
||||
It sends:
|
||||
|
||||
- counters
|
||||
- warnings
|
||||
- timing samples
|
||||
- service health transitions
|
||||
|
||||
It should not use `HealthTelemetry` as a hidden control path.
|
||||
|
||||
### From Snapshot / Telemetry Producers To Web Clients
|
||||
|
||||
`ControlServices` may deliver serialized outbound payloads to WebSocket clients, but the authoritative payload contents should be produced by the owning subsystems.
|
||||
|
||||
That means a later design may look like:
|
||||
|
||||
- `RuntimeSnapshotProvider` provides render-facing snapshot payloads or a runtime-state projection derived from those published snapshots
|
||||
- `RuntimeCoordinator` or a later runtime-read-model helper provides control-plane runtime summaries when the UI needs more than raw render state
|
||||
- `HealthTelemetry` provides health payloads
|
||||
- `ControlServices` delivers them to connected observers
|
||||
|
||||
## Current Code Mapping
|
||||
|
||||
This section maps the current implementation onto the target subsystem.
|
||||
|
||||
### Current `RuntimeServices`
|
||||
|
||||
Should split into:
|
||||
|
||||
- `ControlServices` shell
|
||||
- temporary compatibility adapter into `RuntimeCoordinator`
|
||||
- removal of any direct runtime-state mutation responsibilities over time
|
||||
|
||||
Likely keep under `ControlServices`:
|
||||
|
||||
- service startup/shutdown
|
||||
- OSC update coalescing
|
||||
- Web control hosting shell
|
||||
- file-watch ingress hosting
|
||||
|
||||
Should move out later:
|
||||
|
||||
- direct `RuntimeHost` polling dependency
|
||||
- deferred OSC commit behavior as currently implemented through direct host mutation
|
||||
- any remaining direct state-broadcast decisions tied to runtime internals
|
||||
|
||||
### Current `ControlServer`
|
||||
|
||||
Should become primarily:
|
||||
|
||||
- HTTP ingress adapter
|
||||
- WebSocket session host
|
||||
- static asset/doc host if that remains embedded
|
||||
|
||||
The callback table in current code:
|
||||
|
||||
- [ControlServer.h](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/ControlServer.h:18)
|
||||
|
||||
is a useful migration aid, but long-term it should evolve from callback-per-action toward typed action emission.
|
||||
|
||||
### Current `OscServer`
|
||||
|
||||
Should remain transport-focused.
|
||||
|
||||
Its clean long-term responsibilities are:
|
||||
|
||||
- UDP socket lifecycle
|
||||
- OSC frame decode
|
||||
- syntactic route extraction
|
||||
- emitting decoded automation payloads into the `ControlServices` shell
|
||||
|
||||
It should not own any runtime state semantics beyond ingress decoding.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
The safest migration is incremental.
|
||||
|
||||
### Step 1. Name The Boundary Explicitly
|
||||
|
||||
Create and use the `ControlServices` name in docs and future interfaces before moving all logic.
|
||||
|
||||
This document is part of that step.
|
||||
|
||||
### Step 2. Convert Callback Thinking Into Action Thinking
|
||||
|
||||
Without changing all runtime code at once, introduce typed action/event shapes for the major ingress paths.
|
||||
|
||||
The goal is for transports to emit actions, even if temporary adapters still call into existing code.
|
||||
|
||||
### Step 3. Extract Service Hosting From `OpenGLComposite`
|
||||
|
||||
`OpenGLComposite` currently owns `RuntimeServices` startup and consumption:
|
||||
|
||||
- [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:312)
|
||||
- [OpenGLComposite.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp:723)
|
||||
|
||||
That should move toward a composition root or subsystem host arrangement where render is no longer the owner of control ingress.
|
||||
|
||||
### Step 4. Remove Direct `RuntimeHost` Dependency
|
||||
|
||||
Current polling and deferred OSC commit work directly against `RuntimeHost`:
|
||||
|
||||
- [RuntimeServices.cpp](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/apps/LoopThroughWithOpenGLCompositing/control/RuntimeServices.cpp:194)
|
||||
|
||||
That should be replaced with coordinator-facing actions and later event-driven flows.
|
||||
|
||||
### Step 5. Split Out Observation Delivery
|
||||
|
||||
WebSocket outbound delivery can stay in `ControlServices`, but serialization ownership should move toward the owning subsystems so the service layer stops assembling authoritative state itself.
|
||||
|
||||
## Risks
|
||||
|
||||
### Risk 1. Recreating `RuntimeHost` Coupling Under A New Name
|
||||
|
||||
If `ControlServices` is allowed to keep direct knowledge of runtime mutation internals, it will become a renamed version of the same coupling problem.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- keep the boundary strict
|
||||
- route mutations through coordinator interfaces
|
||||
- treat direct host calls as temporary compatibility only
|
||||
|
||||
### Risk 2. Service Queues Becoming Hidden State Authority
|
||||
|
||||
Latest-value OSC maps and reload debounce flags are appropriate here. Full committed runtime state is not.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- define ingress-local versus authoritative state explicitly
|
||||
- bound queues
|
||||
- publish queue metrics into telemetry
|
||||
|
||||
### Risk 3. WebSocket Broadcast Path Reintroducing Heavy Synchronous Work
|
||||
|
||||
If `ControlServices` becomes the place where whole runtime state is rebuilt or serialized on every input, it will recreate timing stalls.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- broadcast snapshots produced elsewhere
|
||||
- collapse redundant outbound requests
|
||||
- track serialization/broadcast timing in telemetry
|
||||
|
||||
### Risk 4. Polling Surviving Too Long As Architecture
|
||||
|
||||
Some polling may remain during migration, but it should not become the permanent contract.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- isolate polling behind ingress interfaces
|
||||
- make replacement with event-driven flows a planned Phase 2/3 outcome
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should the embedded static UI/docs hosting stay inside `ControlServices`, or move to a thinner app-shell concern while control APIs remain in `ControlServices`?
|
||||
- Should outbound state for WebSocket clients be one combined payload or separate runtime and health channels?
|
||||
- How much route/key resolution should happen in `ControlServices` versus `RuntimeCoordinator`?
|
||||
- Should any deferred automation-settle delivery remain in `ControlServices`, or should all commit/settle policy move entirely into coordinator/render ownership once the live-state model is formalized?
|
||||
- When file watching is modernized, should reload classification live entirely in `ControlServices`, or should it emit a lower-level `FilesChanged` event and let `RuntimeCoordinator` decide reload semantics?
|
||||
- Will future non-OSC automation sources reuse the same `AutomationTargetReceived` path, or need source-specific typed actions for policy reasons?
|
||||
|
||||
## Short Version
|
||||
|
||||
`ControlServices` should become the app's clean ingress boundary:
|
||||
|
||||
- transport handling stays here
|
||||
- input normalization stays here
|
||||
- ingress-local buffering stays here
|
||||
- mutation policy does not
|
||||
- authoritative runtime state does not
|
||||
- render-local transient state does not
|
||||
|
||||
If later phases keep that line sharp, the app gains a control layer that is fast, testable, and timing-aware without becoming another shared state authority.
|
||||
Reference in New Issue
Block a user