Phase 6 step 1
This commit is contained in:
@@ -125,6 +125,7 @@ set(APP_SOURCES
|
|||||||
"${APP_DIR}/runtime/live/RuntimeStateLayerModel.h"
|
"${APP_DIR}/runtime/live/RuntimeStateLayerModel.h"
|
||||||
"${APP_DIR}/runtime/live/RuntimeLiveState.cpp"
|
"${APP_DIR}/runtime/live/RuntimeLiveState.cpp"
|
||||||
"${APP_DIR}/runtime/live/RuntimeLiveState.h"
|
"${APP_DIR}/runtime/live/RuntimeLiveState.h"
|
||||||
|
"${APP_DIR}/runtime/persistence/PersistenceRequest.h"
|
||||||
"${APP_DIR}/runtime/presentation/RuntimeStateJson.cpp"
|
"${APP_DIR}/runtime/presentation/RuntimeStateJson.cpp"
|
||||||
"${APP_DIR}/runtime/presentation/RuntimeStateJson.h"
|
"${APP_DIR}/runtime/presentation/RuntimeStateJson.h"
|
||||||
"${APP_DIR}/runtime/presentation/RuntimeStatePresenter.cpp"
|
"${APP_DIR}/runtime/presentation/RuntimeStatePresenter.cpp"
|
||||||
@@ -184,6 +185,7 @@ target_include_directories(LoopThroughWithOpenGLCompositing PRIVATE
|
|||||||
"${APP_DIR}/runtime/coordination"
|
"${APP_DIR}/runtime/coordination"
|
||||||
"${APP_DIR}/runtime/events"
|
"${APP_DIR}/runtime/events"
|
||||||
"${APP_DIR}/runtime/live"
|
"${APP_DIR}/runtime/live"
|
||||||
|
"${APP_DIR}/runtime/persistence"
|
||||||
"${APP_DIR}/runtime/presentation"
|
"${APP_DIR}/runtime/presentation"
|
||||||
"${APP_DIR}/runtime/snapshot"
|
"${APP_DIR}/runtime/snapshot"
|
||||||
"${APP_DIR}/runtime/store"
|
"${APP_DIR}/runtime/store"
|
||||||
@@ -293,6 +295,7 @@ target_include_directories(RuntimeEventTypeTests PRIVATE
|
|||||||
"${APP_DIR}"
|
"${APP_DIR}"
|
||||||
"${APP_DIR}/runtime"
|
"${APP_DIR}/runtime"
|
||||||
"${APP_DIR}/runtime/events"
|
"${APP_DIR}/runtime/events"
|
||||||
|
"${APP_DIR}/runtime/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
@@ -366,6 +369,7 @@ target_include_directories(RuntimeSubsystemTests PRIVATE
|
|||||||
"${APP_DIR}/runtime/coordination"
|
"${APP_DIR}/runtime/coordination"
|
||||||
"${APP_DIR}/runtime/events"
|
"${APP_DIR}/runtime/events"
|
||||||
"${APP_DIR}/runtime/live"
|
"${APP_DIR}/runtime/live"
|
||||||
|
"${APP_DIR}/runtime/persistence"
|
||||||
"${APP_DIR}/runtime/presentation"
|
"${APP_DIR}/runtime/presentation"
|
||||||
"${APP_DIR}/runtime/snapshot"
|
"${APP_DIR}/runtime/snapshot"
|
||||||
"${APP_DIR}/runtime/store"
|
"${APP_DIR}/runtime/store"
|
||||||
|
|||||||
@@ -580,8 +580,7 @@ void RuntimeCoordinator::PublishCoordinatorFollowUpEvents(const std::string& act
|
|||||||
if (result.persistenceRequested)
|
if (result.persistenceRequested)
|
||||||
{
|
{
|
||||||
RuntimePersistenceRequestedEvent persistenceRequested;
|
RuntimePersistenceRequestedEvent persistenceRequested;
|
||||||
persistenceRequested.reason = action;
|
persistenceRequested.request = PersistenceRequest::RuntimeStateRequest(action);
|
||||||
persistenceRequested.debounceAllowed = true;
|
|
||||||
mRuntimeEventDispatcher.PublishPayload(persistenceRequested, "RuntimeCoordinator");
|
mRuntimeEventDispatcher.PublishPayload(persistenceRequested, "RuntimeCoordinator");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include "PersistenceRequest.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
enum class RuntimeEventSeverity
|
enum class RuntimeEventSeverity
|
||||||
@@ -109,8 +111,7 @@ struct RuntimeStateChangedEvent
|
|||||||
|
|
||||||
struct RuntimePersistenceRequestedEvent
|
struct RuntimePersistenceRequestedEvent
|
||||||
{
|
{
|
||||||
std::string reason;
|
PersistenceRequest request;
|
||||||
bool debounceAllowed = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RuntimeReloadRequestedEvent
|
struct RuntimeReloadRequestedEvent
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
enum class PersistenceTargetKind
|
||||||
|
{
|
||||||
|
RuntimeState,
|
||||||
|
StackPreset,
|
||||||
|
RuntimeConfig
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PersistenceRequest
|
||||||
|
{
|
||||||
|
PersistenceTargetKind targetKind = PersistenceTargetKind::RuntimeState;
|
||||||
|
std::string reason;
|
||||||
|
std::string debounceKey = "runtime-state";
|
||||||
|
bool debounceAllowed = true;
|
||||||
|
bool flushRequested = false;
|
||||||
|
uint64_t sequence = 0;
|
||||||
|
|
||||||
|
static PersistenceRequest RuntimeStateRequest(const std::string& reason)
|
||||||
|
{
|
||||||
|
PersistenceRequest request;
|
||||||
|
request.targetKind = PersistenceTargetKind::RuntimeState;
|
||||||
|
request.reason = reason;
|
||||||
|
request.debounceKey = "runtime-state";
|
||||||
|
request.debounceAllowed = true;
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PersistenceSnapshot
|
||||||
|
{
|
||||||
|
PersistenceTargetKind targetKind = PersistenceTargetKind::RuntimeState;
|
||||||
|
std::filesystem::path targetPath;
|
||||||
|
std::string contents;
|
||||||
|
std::string reason;
|
||||||
|
uint64_t generation = 0;
|
||||||
|
};
|
||||||
@@ -7,15 +7,16 @@ Phases 1-5 separate durable state, coordination policy, render-facing snapshots,
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
- Phase 6 design package: proposed.
|
- Phase 6 design package: proposed.
|
||||||
- Phase 6 implementation: not started.
|
- Phase 6 implementation: Step 1 complete.
|
||||||
- Current alignment: `RuntimeStore` owns durable serialization, config, package metadata, preset IO, and persistence requests; `CommittedLiveState` owns the current committed/session layer state; and `RuntimeCoordinator` already publishes explicit persistence-request outcomes for persisted mutations. The remaining issue is that actual disk writes are still synchronous store work rather than queued, debounced, atomic background writes.
|
- Current alignment: `RuntimeStore` owns durable serialization, config, package metadata, preset IO, and persistence requests; `CommittedLiveState` owns the current committed/session layer state; and `RuntimeCoordinator` publishes typed persistence requests for persisted mutations. The remaining issue is that actual disk writes are still synchronous store work rather than queued, debounced, atomic background writes.
|
||||||
|
|
||||||
Current persistence footholds:
|
Current persistence footholds:
|
||||||
|
|
||||||
- `RuntimeStore` owns persistent runtime-state serialization, stack preset serialization, and durable file IO.
|
- `RuntimeStore` owns persistent runtime-state serialization, stack preset serialization, and durable file IO.
|
||||||
- `CommittedLiveState` owns current committed/session layer and parameter state.
|
- `CommittedLiveState` owns current committed/session layer and parameter state.
|
||||||
- `RuntimeCoordinatorResult::persistenceRequested` exists as an explicit mutation outcome.
|
- `RuntimeCoordinatorResult::persistenceRequested` exists as an explicit mutation outcome.
|
||||||
- `RuntimeEventType::RuntimePersistenceRequested` exists as the event-level persistence request.
|
- `RuntimeEventType::RuntimePersistenceRequested` now carries a `PersistenceRequest`.
|
||||||
|
- `PersistenceRequest` and `PersistenceSnapshot` name the request/snapshot contract that later steps will hand to the writer.
|
||||||
- Phase 5 clarified which live-state mutations are durable, committed-live, transient automation, or render-local. Settled OSC commits are session-only by default and do not request persistence.
|
- Phase 5 clarified which live-state mutations are durable, committed-live, transient automation, or render-local. Settled OSC commits are session-only by default and do not request persistence.
|
||||||
|
|
||||||
## Why Phase 6 Exists
|
## Why Phase 6 Exists
|
||||||
@@ -183,9 +184,16 @@ Make request types and event payloads explicit enough that callers stop thinking
|
|||||||
|
|
||||||
Initial target:
|
Initial target:
|
||||||
|
|
||||||
- keep existing coordinator persistence decisions
|
- [x] keep existing coordinator persistence decisions
|
||||||
- introduce a `PersistenceRequest`/`PersistenceSnapshot` shape
|
- [x] introduce a `PersistenceRequest`/`PersistenceSnapshot` shape
|
||||||
- document which requests are debounceable
|
- [x] document which requests are debounceable
|
||||||
|
|
||||||
|
Current implementation:
|
||||||
|
|
||||||
|
- `runtime/persistence/PersistenceRequest.h` defines `PersistenceTargetKind`, `PersistenceRequest`, and `PersistenceSnapshot`.
|
||||||
|
- `RuntimePersistenceRequestedEvent` carries a typed `PersistenceRequest`.
|
||||||
|
- `RuntimeCoordinator` emits runtime-state persistence requests with reason, debounce key, and debounce policy.
|
||||||
|
- Existing synchronous save behavior is intentionally unchanged until Step 2/3.
|
||||||
|
|
||||||
### Step 2. Extract Snapshot Writing From `RuntimeStore`
|
### Step 2. Extract Snapshot Writing From `RuntimeStore`
|
||||||
|
|
||||||
|
|||||||
@@ -62,10 +62,19 @@ void TestRuntimeEventPayloadTypes()
|
|||||||
Expect(rejectedMutation.errorMessage == "Unknown layer.", "mutation payload carries rejection error");
|
Expect(rejectedMutation.errorMessage == "Unknown layer.", "mutation payload carries rejection error");
|
||||||
|
|
||||||
RuntimePersistenceRequestedEvent persistence;
|
RuntimePersistenceRequestedEvent persistence;
|
||||||
persistence.reason = "UpdateLayerParameter";
|
persistence.request = PersistenceRequest::RuntimeStateRequest("UpdateLayerParameter");
|
||||||
persistence.debounceAllowed = true;
|
|
||||||
Expect(RuntimeEventPayloadType(persistence) == RuntimeEventType::RuntimePersistenceRequested, "runtime persistence payload maps to persistence event type");
|
Expect(RuntimeEventPayloadType(persistence) == RuntimeEventType::RuntimePersistenceRequested, "runtime persistence payload maps to persistence event type");
|
||||||
Expect(persistence.debounceAllowed, "runtime persistence payload carries debounce policy");
|
Expect(persistence.request.targetKind == PersistenceTargetKind::RuntimeState, "runtime persistence payload carries target kind");
|
||||||
|
Expect(persistence.request.reason == "UpdateLayerParameter", "runtime persistence payload carries request reason");
|
||||||
|
Expect(persistence.request.debounceAllowed, "runtime persistence payload carries debounce policy");
|
||||||
|
Expect(persistence.request.debounceKey == "runtime-state", "runtime persistence payload carries debounce key");
|
||||||
|
|
||||||
|
PersistenceSnapshot persistenceSnapshot;
|
||||||
|
persistenceSnapshot.targetKind = PersistenceTargetKind::RuntimeState;
|
||||||
|
persistenceSnapshot.reason = persistence.request.reason;
|
||||||
|
persistenceSnapshot.contents = "{}";
|
||||||
|
Expect(persistenceSnapshot.reason == "UpdateLayerParameter", "persistence snapshot carries capture reason");
|
||||||
|
Expect(persistenceSnapshot.contents == "{}", "persistence snapshot carries serialized content");
|
||||||
|
|
||||||
FileChangeDetectedEvent fileChange;
|
FileChangeDetectedEvent fileChange;
|
||||||
fileChange.path = "PollRuntimeStoreChanges";
|
fileChange.path = "PollRuntimeStoreChanges";
|
||||||
@@ -459,8 +468,7 @@ void TestAcceptedMutationFollowUps()
|
|||||||
stateChanged.persistenceRequested = true;
|
stateChanged.persistenceRequested = true;
|
||||||
|
|
||||||
RuntimePersistenceRequestedEvent persistence;
|
RuntimePersistenceRequestedEvent persistence;
|
||||||
persistence.reason = mutation.action;
|
persistence.request = PersistenceRequest::RuntimeStateRequest(mutation.action);
|
||||||
persistence.debounceAllowed = true;
|
|
||||||
|
|
||||||
RuntimeReloadRequestedEvent reload;
|
RuntimeReloadRequestedEvent reload;
|
||||||
reload.reason = mutation.action;
|
reload.reason = mutation.action;
|
||||||
@@ -487,7 +495,8 @@ void TestAcceptedMutationFollowUps()
|
|||||||
|
|
||||||
const RuntimeEvent* persistenceEvent = harness.LastSeen(RuntimeEventType::RuntimePersistenceRequested);
|
const RuntimeEvent* persistenceEvent = harness.LastSeen(RuntimeEventType::RuntimePersistenceRequested);
|
||||||
const auto* persistencePayload = persistenceEvent ? std::get_if<RuntimePersistenceRequestedEvent>(&persistenceEvent->payload) : nullptr;
|
const auto* persistencePayload = persistenceEvent ? std::get_if<RuntimePersistenceRequestedEvent>(&persistenceEvent->payload) : nullptr;
|
||||||
Expect(persistencePayload && persistencePayload->reason == "SetLayerShader", "persistence follow-up preserves mutation action reason");
|
Expect(persistencePayload && persistencePayload->request.reason == "SetLayerShader", "persistence follow-up preserves mutation action reason");
|
||||||
|
Expect(persistencePayload && persistencePayload->request.debounceKey == "runtime-state", "persistence follow-up preserves debounce key");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestAppLevelBroadcastAndBuildCoalescing()
|
void TestAppLevelBroadcastAndBuildCoalescing()
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ void TestRuntimeCoordinatorPersistenceEvents()
|
|||||||
if (event.type != RuntimeEventType::RuntimePersistenceRequested)
|
if (event.type != RuntimeEventType::RuntimePersistenceRequested)
|
||||||
continue;
|
continue;
|
||||||
const auto* payload = std::get_if<RuntimePersistenceRequestedEvent>(&event.payload);
|
const auto* payload = std::get_if<RuntimePersistenceRequestedEvent>(&event.payload);
|
||||||
return payload ? payload->reason : std::string();
|
return payload ? payload->request.reason : std::string();
|
||||||
}
|
}
|
||||||
return std::string();
|
return std::string();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user