Step 4
This commit is contained in:
@@ -241,6 +241,7 @@ void ControlServices::PollLoop(RuntimeCoordinator& runtimeCoordinator)
|
|||||||
completedCommit.generation = entry.second.generation;
|
completedCommit.generation = entry.second.generation;
|
||||||
std::lock_guard<std::mutex> lock(mCompletedOscCommitMutex);
|
std::lock_guard<std::mutex> lock(mCompletedOscCommitMutex);
|
||||||
mCompletedOscCommits.push_back(std::move(completedCommit));
|
mCompletedOscCommits.push_back(std::move(completedCommit));
|
||||||
|
PublishOscOverlaySettled(entry.second);
|
||||||
}
|
}
|
||||||
else if (!result.errorMessage.empty())
|
else if (!result.errorMessage.empty())
|
||||||
{
|
{
|
||||||
@@ -321,3 +322,22 @@ void ControlServices::PublishOscCommitRequested(const PendingOscCommit& commit)
|
|||||||
OutputDebugStringA("OscCommitRequested event publish threw.\n");
|
OutputDebugStringA("OscCommitRequested event publish threw.\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlServices::PublishOscOverlaySettled(const PendingOscCommit& commit)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OscOverlayEvent event;
|
||||||
|
event.routeKey = commit.routeKey;
|
||||||
|
event.layerKey = commit.layerKey;
|
||||||
|
event.parameterKey = commit.parameterKey;
|
||||||
|
event.generation = commit.generation;
|
||||||
|
event.settled = true;
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "ControlServices"))
|
||||||
|
OutputDebugStringA("OscOverlaySettled event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("OscOverlaySettled event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ private:
|
|||||||
void PublishRuntimeStateBroadcastRequested(const std::string& reason);
|
void PublishRuntimeStateBroadcastRequested(const std::string& reason);
|
||||||
void PublishOscValueReceived(const PendingOscUpdate& update, const std::string& routeKey);
|
void PublishOscValueReceived(const PendingOscUpdate& update, const std::string& routeKey);
|
||||||
void PublishOscCommitRequested(const PendingOscCommit& commit);
|
void PublishOscCommitRequested(const PendingOscCommit& commit);
|
||||||
|
void PublishOscOverlaySettled(const PendingOscCommit& commit);
|
||||||
|
|
||||||
std::unique_ptr<ControlServer> mControlServer;
|
std::unique_ptr<ControlServer> mControlServer;
|
||||||
std::unique_ptr<OscServer> mOscServer;
|
std::unique_ptr<OscServer> mOscServer;
|
||||||
|
|||||||
@@ -178,9 +178,14 @@ RuntimeCoordinatorResult RuntimeCoordinator::UpdateLayerParameterByControlKey(co
|
|||||||
RuntimeCoordinatorResult RuntimeCoordinator::CommitOscParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue)
|
RuntimeCoordinatorResult RuntimeCoordinator::CommitOscParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
constexpr RuntimeCoordinatorOscCommitPersistence kDefaultOscCommitPersistence =
|
||||||
|
RuntimeCoordinatorOscCommitPersistence::SessionOnly;
|
||||||
|
constexpr bool kPersistSettledOscCommits =
|
||||||
|
kDefaultOscCommitPersistence == RuntimeCoordinatorOscCommitPersistence::Persistent;
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
ResolvedParameterMutation mutation;
|
ResolvedParameterMutation mutation;
|
||||||
if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, true, mutation, error))
|
if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, kPersistSettledOscCommits, mutation, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("CommitOscParameterByControlKey", result);
|
PublishCoordinatorResult("CommitOscParameterByControlKey", result);
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ enum class RuntimeCoordinatorTransientOscInvalidation
|
|||||||
All
|
All
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class RuntimeCoordinatorOscCommitPersistence
|
||||||
|
{
|
||||||
|
SessionOnly,
|
||||||
|
Persistent
|
||||||
|
};
|
||||||
|
|
||||||
struct RuntimeCoordinatorResult
|
struct RuntimeCoordinatorResult
|
||||||
{
|
{
|
||||||
bool accepted = false;
|
bool accepted = false;
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ Phase 1 named the subsystems. Phase 2 added the typed event substrate. Phase 3 m
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
- Phase 5 design package: proposed.
|
- Phase 5 design package: proposed.
|
||||||
- Phase 5 implementation: Step 3 complete.
|
- Phase 5 implementation: Step 4 complete.
|
||||||
- Current alignment: Phase 3 introduced the first pure composition boundary and transient OSC overlay owner. Phase 5 now has a small `RuntimeStateLayerModel` inventory that names the current state categories, `RenderStateComposer` consumes a `LayeredRenderStateInput` whose fields make base persisted, committed live, and transient automation inputs explicit, and `RuntimeLiveState` owns transient-overlay invalidation against current layer/parameter compatibility. Committed runtime values are still physically stored through `RuntimeStore`/`LayerStackStore`.
|
- Current alignment: Phase 3 introduced the first pure composition boundary and transient OSC overlay owner. Phase 5 now has a small `RuntimeStateLayerModel` inventory that names the current state categories, `RenderStateComposer` consumes a `LayeredRenderStateInput` whose fields make base persisted, committed live, and transient automation inputs explicit, `RuntimeLiveState` owns transient-overlay invalidation against current layer/parameter compatibility, and settled OSC commits have an explicit session-only persistence policy. Committed runtime values are still physically stored through `RuntimeStore`/`LayerStackStore`.
|
||||||
|
|
||||||
Current live-state footholds:
|
Current live-state footholds:
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ Current live-state footholds:
|
|||||||
- `RuntimeServiceLiveBridge` drains OSC ingress/completion queues and applies them to render live state during frame preparation.
|
- `RuntimeServiceLiveBridge` drains OSC ingress/completion queues and applies them to render live state during frame preparation.
|
||||||
- `RuntimeStateLayerModel` names the Phase 5 state categories and classifies current fields as base persisted, committed live, transient automation, render-local, or health/config state.
|
- `RuntimeStateLayerModel` names the Phase 5 state categories and classifies current fields as base persisted, committed live, transient automation, render-local, or health/config state.
|
||||||
- `RuntimeCoordinator` can request layer-scoped transient OSC invalidation, while `RuntimeLiveState` prunes overlays that no longer map to the current render-facing layer/parameter definitions.
|
- `RuntimeCoordinator` can request layer-scoped transient OSC invalidation, while `RuntimeLiveState` prunes overlays that no longer map to the current render-facing layer/parameter definitions.
|
||||||
|
- `RuntimeCoordinator::CommitOscParameterByControlKey(...)` commits settled OSC values into session state without requesting persistence by default.
|
||||||
|
|
||||||
## Why Phase 5 Exists
|
## Why Phase 5 Exists
|
||||||
|
|
||||||
@@ -287,13 +288,22 @@ Make the transient-to-committed path explicit.
|
|||||||
|
|
||||||
Initial target:
|
Initial target:
|
||||||
|
|
||||||
- document and test whether settled OSC commits persist
|
- [x] document and test whether settled OSC commits persist
|
||||||
- ensure stale generation completions are ignored
|
- [x] ensure stale generation completions are ignored
|
||||||
- ensure one settled route does not clear unrelated overlay state
|
- [x] ensure one settled route does not clear unrelated overlay state
|
||||||
- publish or preserve useful events for accepted overlay commits
|
- [x] publish or preserve useful events for accepted overlay commits
|
||||||
|
|
||||||
Current Phase 3 behavior is a good base; Phase 5 should make the policy easier to reason about from the code.
|
Current Phase 3 behavior is a good base; Phase 5 should make the policy easier to reason about from the code.
|
||||||
|
|
||||||
|
Current policy:
|
||||||
|
|
||||||
|
- settled OSC commits are `RuntimeCoordinatorOscCommitPersistence::SessionOnly` by default
|
||||||
|
- accepted settled OSC commits update the committed session value through `RuntimeStore::SetStoredParameterValue(..., persistState = false, ...)`
|
||||||
|
- accepted settled OSC commits publish runtime mutation/state-change observations, but no `RuntimePersistenceRequested` event
|
||||||
|
- accepted service-side commit completions publish `OscOverlaySettled`
|
||||||
|
- stale generation completions are ignored by `RuntimeLiveState::ApplyOscCommitCompletions(...)`
|
||||||
|
- unrelated routes remain untouched when a different route settles or completes
|
||||||
|
|
||||||
### Step 5. Separate Committed-Live Concept From Durable Storage
|
### Step 5. Separate Committed-Live Concept From Durable Storage
|
||||||
|
|
||||||
Decide whether to physically split committed-live state now or introduce a read/model boundary first.
|
Decide whether to physically split committed-live state now or introduce a read/model boundary first.
|
||||||
@@ -377,7 +387,7 @@ Phase 5 can be considered complete once the project can say:
|
|||||||
- [x] final render-value precedence is explicit and covered by tests
|
- [x] final render-value precedence is explicit and covered by tests
|
||||||
- [x] `RenderStateComposer` or its replacement consumes a layered input contract
|
- [x] `RenderStateComposer` or its replacement consumes a layered input contract
|
||||||
- [x] reset/reload/preset behavior for transient overlays is centralized or clearly delegated
|
- [x] reset/reload/preset behavior for transient overlays is centralized or clearly delegated
|
||||||
- [ ] OSC overlay settle/commit behavior is explicit, including persistence policy
|
- [x] OSC overlay settle/commit behavior is explicit, including persistence policy
|
||||||
- [ ] `RuntimeStore` remains durable-state focused and does not absorb transient automation policy
|
- [ ] `RuntimeStore` remains durable-state focused and does not absorb transient automation policy
|
||||||
- [ ] render-local temporal/feedback state remains separate from live parameter layering
|
- [ ] render-local temporal/feedback state remains separate from live parameter layering
|
||||||
- [ ] subsystem docs and the architecture review reflect the final ownership model
|
- [ ] subsystem docs and the architecture review reflect the final ownership model
|
||||||
|
|||||||
@@ -272,6 +272,8 @@ For OSC specifically, the coordinator should eventually decide:
|
|||||||
- whether it should later commit into committed live state
|
- whether it should later commit into committed live state
|
||||||
- what reset/reload actions invalidate it
|
- what reset/reload actions invalidate it
|
||||||
|
|
||||||
|
Phase 5 sets the default settled OSC policy to session-only. `CommitOscParameterByControlKey(...)` updates committed session state through the store with persistence disabled, publishes ordinary mutation/state-change observations, and does not request a persistence write unless a future explicit policy opts into durable OSC commits.
|
||||||
|
|
||||||
### Health and timing state
|
### Health and timing state
|
||||||
|
|
||||||
The coordinator may emit events like:
|
The coordinator may emit events like:
|
||||||
|
|||||||
@@ -311,8 +311,18 @@ void TestRuntimeCoordinatorPersistenceEvents()
|
|||||||
Expect(countEvents(overlayEvents, RuntimeEventType::OscOverlayApplied) == 1, "transient OSC overlay is observable");
|
Expect(countEvents(overlayEvents, RuntimeEventType::OscOverlayApplied) == 1, "transient OSC overlay is observable");
|
||||||
Expect(countEvents(overlayEvents, RuntimeEventType::RuntimePersistenceRequested) == 0, "transient OSC overlay does not request persistence");
|
Expect(countEvents(overlayEvents, RuntimeEventType::RuntimePersistenceRequested) == 0, "transient OSC overlay does not request persistence");
|
||||||
|
|
||||||
expectAcceptedPersistence(coordinator.CommitOscParameterByControlKey("alpha", "gain", JsonValue(0.2)), "CommitOscParameterByControlKey",
|
RuntimeCoordinatorResult oscCommitResult = coordinator.CommitOscParameterByControlKey("alpha", "gain", JsonValue(0.2));
|
||||||
"accepted OSC commit is persistent");
|
std::vector<RuntimeEvent> oscCommitEvents = dispatchAndClear();
|
||||||
|
Expect(oscCommitResult.accepted, "accepted OSC commit updates committed session state");
|
||||||
|
Expect(!oscCommitResult.persistenceRequested, "settled OSC commit does not request persistence by default");
|
||||||
|
Expect(countEvents(oscCommitEvents, RuntimeEventType::RuntimeMutationAccepted) == 1, "settled OSC commit publishes accepted fact");
|
||||||
|
Expect(countEvents(oscCommitEvents, RuntimeEventType::RuntimeStateChanged) == 1, "settled OSC commit publishes state change");
|
||||||
|
Expect(countEvents(oscCommitEvents, RuntimeEventType::RuntimePersistenceRequested) == 0, "settled OSC commit publishes no persistence request");
|
||||||
|
RuntimeStore::StoredParameterSnapshot oscCommitSnapshot;
|
||||||
|
Expect(store.TryGetStoredParameterByControlKey("alpha", "gain", oscCommitSnapshot, error), "settled OSC commit can be read back");
|
||||||
|
Expect(!oscCommitSnapshot.currentValue.numberValues.empty() &&
|
||||||
|
oscCommitSnapshot.currentValue.numberValues[0] == 0.2,
|
||||||
|
"settled OSC commit updates the committed session value");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::remove_all(root);
|
std::filesystem::remove_all(root);
|
||||||
|
|||||||
Reference in New Issue
Block a user