diff --git a/docs/ARCHITECTURE_RESILIENCE_REVIEW.md b/docs/ARCHITECTURE_RESILIENCE_REVIEW.md index e482ae2..d623c57 100644 --- a/docs/ARCHITECTURE_RESILIENCE_REVIEW.md +++ b/docs/ARCHITECTURE_RESILIENCE_REVIEW.md @@ -10,7 +10,7 @@ Phase checklist: - [x] Finish live-state and service-facing coordination - [x] Make the render thread the sole GL owner - [x] Refactor live state layering into an explicit composition model -- [ ] Move persistence onto a background snapshot writer +- [x] Move persistence onto a background snapshot writer - [ ] Make DeckLink/backend lifecycle explicit with a state machine - [ ] Add structured health, telemetry, and operational reporting @@ -21,7 +21,7 @@ Checklist note: - The checked Phase 3 item means the render-facing state path now has named live-state, composition, frame-state, resolver, and service-bridge boundaries. `OpenGLComposite::renderEffect()` is reduced to runtime work, frame input construction, and frame rendering. - The checked Phase 4 item means normal runtime GL work is now owned by a dedicated `RenderEngine` render thread. Input upload, output render, preview, screenshot capture, render-local resets, and shader application enter through render-thread queue/request paths instead of caller-thread context borrowing. The remaining output timing risk is callback-coupled synchronous output production, which is intentionally tracked for the later DeckLink/backend lifecycle and playout-queue work. - The checked Phase 5 item means persisted, committed/session, transient automation, and render-local state are explicitly named. `CommittedLiveState` physically owns current session layer state, `RuntimeLiveState` owns transient OSC overlays, `RenderStateComposer` consumes a layered input contract, and reset/reload/preset overlay invalidation is centralized and covered by non-GL tests. -- It does not mean the whole app is fully extracted. Background persistence, backend lifecycle/playout queue policy, and richer telemetry continue through later phases. +- It does not mean the whole app is fully extracted. Backend lifecycle/playout queue policy and richer telemetry continue through later phases. ## Timing Review @@ -575,18 +575,19 @@ Expected benefits: ### Phase 6. Move persistence onto a background snapshot writer -After the state model is explicit, persistence should become a background concern rather than a synchronous side effect of mutations. +Status: complete. Runtime-state persistence is now a background concern rather than a synchronous side effect of mutations. Dedicated design note: - [PHASE_6_BACKGROUND_PERSISTENCE_DESIGN.md](/c:/Users/Aiden/Documents/GitHub/video-shader-toys/docs/PHASE_6_BACKGROUND_PERSISTENCE_DESIGN.md) -Target behavior: +Implemented behavior: - mutations update authoritative in-memory stored state - persistence requests are queued - disk writes are debounced and coalesced -- writes are atomic and versioned where practical +- writes use temp-file replacement where practical +- shutdown flush behavior is explicit and tested Why this phase comes after state splitting: @@ -688,7 +689,7 @@ This order tries to avoid doing foundational work twice. - The event model comes before major subsystem extraction so coordination patterns stabilize early. - runtime state ownership is split before render isolation so the render thread does not inherit a monolithic state model. - Live state layering is formalized only after render ownership is clearer. -- Persistence is moved later so it can target the final state model rather than the current one. +- Persistence moved after the state model split so it could target the durable snapshot model rather than an older mixed-responsibility runtime object. - Telemetry is intentionally late so it instruments the architecture that survives the refactor. ## Short Version @@ -700,7 +701,7 @@ The app is in a much better place than it was before the OSC timing work. The sh 3. split state ownership 4. isolate rendering 5. formalize layered live state -6. background persistence +6. complete background persistence 7. explicit backend lifecycle 8. health and telemetry diff --git a/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md b/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md index 3f54f18..829f118 100644 --- a/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md +++ b/docs/PHASE_1_SUBSYSTEM_BOUNDARIES_DESIGN.md @@ -504,7 +504,8 @@ Core responsibilities: - `LoadConfig()` - `LoadPersistentState()` -- `SavePersistentStateSnapshot(...)` +- `BuildPersistentStateSnapshot(...)` +- `RequestPersistence(...)` - `GetStoredLayerStack()` - `SetStoredLayerStack(...)` - `GetStackPresetNames()` diff --git a/docs/PHASE_3_LIVE_STATE_SERVICE_COORDINATION_DESIGN.md b/docs/PHASE_3_LIVE_STATE_SERVICE_COORDINATION_DESIGN.md index 7b795e8..c3c1569 100644 --- a/docs/PHASE_3_LIVE_STATE_SERVICE_COORDINATION_DESIGN.md +++ b/docs/PHASE_3_LIVE_STATE_SERVICE_COORDINATION_DESIGN.md @@ -35,7 +35,7 @@ The main problems Phase 3 addressed: - transient OSC overlay state and persisted committed state needed a named reconciliation boundary - `RenderEngine` needed to move final frame-state selection and value composition out of drawing code - service-side queues for pending OSC updates and completed OSC commits needed a bridge outside `OpenGLComposite` -- `RuntimeStore` still performs synchronous persistence directly from many state mutation paths +- Phase 6 has since moved runtime-state persistence requests onto a debounced background writer - `RuntimeUpdateController` still exists partly as compatibility glue between synchronous coordinator results and event-driven effects ## Goals @@ -46,7 +46,7 @@ Phase 3 should establish: - service-facing event bridges for OSC overlay updates and overlay commit completions - a narrower `OpenGLComposite::renderEffect()` that renders a prepared read model instead of orchestrating runtime/service state - a clear owner for final render-layer state resolution before it reaches GL drawing -- a contained persistence request model that prepares for the later background writer phase +- a contained persistence request model that later Phase 6 connected to the background writer - tests for live-state composition, overlay settlement, and service-to-runtime event behavior without GL or DeckLink ## Non-Goals @@ -153,7 +153,7 @@ Non-responsibilities: | render asks for overlay commit requests | `OscOverlaySettled` or direct coordinator command plus event publication | Commit request creation should leave `renderEffect()` and live near the live-state owner. | | completed OSC commits drained by `OpenGLComposite` | `RuntimeMutationAccepted` / completion event -> live-state commit completion | Completed commit routing should be event-driven or owned by live-state service bridge. | | `RenderFrameStateResolver::Resolve(...)` | `RenderStateComposer::BuildFrameState(...)` | Keep final state composition testable without GL. | -| direct persistence writes from store mutations | `RuntimePersistenceRequested` as the durable write trigger | Background writer lands later; Phase 3 should make request boundaries clear. | +| direct persistence writes from store mutations | `RuntimePersistenceRequested` as the durable write trigger | Phase 6 later connected this request boundary to the background writer. | | runtime-state broadcast side effects | `RuntimeStateBroadcastRequested` plus optional completed/failed observations | Keep broadcast delivery in services and presentation ownership in runtime presentation. | ## Runtime Store Scope In Phase 3 @@ -164,7 +164,7 @@ Target responsibilities: - initialize runtime config and persistent state - expose durable layer/package/config read models -- own saved layer stack and preset serialization until the background writer phase +- own saved layer stack and preset serialization while exposing snapshots for the background writer - publish or support immutable render/presentation snapshots Avoid adding: @@ -231,16 +231,16 @@ Avoid: ## Persistence Position -Phase 3 should not implement the background writer, but it should prepare for it. +Phase 3 did not implement the background writer, but it prepared the request boundary that Phase 6 now uses. Target behavior by Phase 3 exit: - state mutations publish `RuntimePersistenceRequested` - persistence can be observed and tested as an event side effect -- synchronous `SavePersistentState()` remains allowed as an implementation detail inside `RuntimeStore` +- disk writes are not inferred by callers; later Phase 6 routes accepted durable mutations through `RuntimePersistenceRequested` and the background writer - callers outside the store/coordinator should not infer disk writes from mutation categories -This keeps Phase 6 smaller: the background snapshot writer can subscribe to persistence requests and consume a stored-state snapshot rather than rediscovering mutation policy. +This kept Phase 6 smaller: the background snapshot writer consumes persistence requests and stored-state snapshots rather than rediscovering mutation policy. ## Migration Plan @@ -348,7 +348,7 @@ The current groundwork is intended to let these lanes proceed in parallel with l | B. Render-state composition | `runtime/live/RenderStateComposer.*`, `gl/frame/RenderFrameStateResolver.*`, `gl/RenderEngine.*` | Implemented for Phase 3: value composition and frame-state selection sit outside GL drawing while GL calls remain in `RenderEngine`. | | C. Service bridge | `control/RuntimeServices.*`, `control/RuntimeServiceLiveBridge.*`, `control/ControlServices.*` | Implemented for Phase 3: `OpenGLComposite::renderEffect()` no longer drains OSC update/completion queues directly. | | D. App-frame orchestration | `gl/composite/OpenGLComposite.*`, `gl/frame/RuntimeUpdateController.*` | Implemented for Phase 3: render-effect glue is a narrow runtime-work, frame-input, render-frame sequence. | -| E. Persistence boundary | `runtime/coordination/RuntimeCoordinator.*`, `runtime/store/*`, event tests | Implemented for Phase 3: persistence request publication is explicit and ready for a later background writer. | +| E. Persistence boundary | `runtime/coordination/RuntimeCoordinator.*`, `runtime/store/*`, event tests | Implemented for Phase 3: persistence request publication is explicit. Phase 6 later wired those requests to the background writer. | ## Phase 3 Exit Criteria @@ -368,7 +368,7 @@ Phase 3 can be considered complete once the project can say: - Should the new live-state owner live under `runtime/`, `gl/`, or a new `renderstate/` boundary? - Should smoothing policy be owned by live state, render-state composition, or render settings? - Should overlay commit completion be represented as a new typed event, or derived from existing accepted mutation events with route/generation metadata? -- How much of persistence should remain synchronous until Phase 6? +- Should preset save remain synchronous after Phase 6, or eventually move behind a completion-based async request? ## Short Version