diff --git a/ROADMAP.md b/ROADMAP.md index 17a6722..087ed72 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,7 @@ # ROADMAP +See docs/PROMPT.md + ## North Star Treat JSON config as a declarative control plane that compiles into scene, resource, and render graphs with strict validation, budget enforcement, and crash-resistant policies. diff --git a/src/app/service_based_app.cpp b/src/app/service_based_app.cpp index eeeb7e3..c943892 100644 --- a/src/app/service_based_app.cpp +++ b/src/app/service_based_app.cpp @@ -311,7 +311,8 @@ void ServiceBasedApp::RegisterServices() { registry_.GetService(), registry_.GetService(), registry_.GetService(), - registry_.GetService()); + registry_.GetService(), + registry_.GetService()); // Script bridge services registry_.RegisterService( diff --git a/src/services/impl/frame_workflow_service.cpp b/src/services/impl/frame_workflow_service.cpp index a5d9873..0fd4ad2 100644 --- a/src/services/impl/frame_workflow_service.cpp +++ b/src/services/impl/frame_workflow_service.cpp @@ -14,6 +14,7 @@ FrameWorkflowService::FrameWorkflowService(std::shared_ptr logger, std::shared_ptr physicsService, std::shared_ptr sceneService, std::shared_ptr renderService, + std::shared_ptr validationTourService, const std::filesystem::path& templatePath) : registry_(std::make_shared()), executor_(registry_, logger), @@ -30,7 +31,8 @@ FrameWorkflowService::FrameWorkflowService(std::shared_ptr logger, std::move(inputService), std::move(physicsService), std::move(sceneService), - std::move(renderService)); + std::move(renderService), + std::move(validationTourService)); registrar.RegisterUsedSteps(workflow_, registry_); } diff --git a/src/services/impl/frame_workflow_service.hpp b/src/services/impl/frame_workflow_service.hpp index c0e5866..364d0f7 100644 --- a/src/services/impl/frame_workflow_service.hpp +++ b/src/services/impl/frame_workflow_service.hpp @@ -7,6 +7,7 @@ #include "../interfaces/i_physics_service.hpp" #include "../interfaces/i_render_coordinator_service.hpp" #include "../interfaces/i_scene_service.hpp" +#include "../interfaces/i_validation_tour_service.hpp" #include "workflow_executor.hpp" #include "workflow_definition_parser.hpp" @@ -25,6 +26,7 @@ public: std::shared_ptr physicsService, std::shared_ptr sceneService, std::shared_ptr renderService, + std::shared_ptr validationTourService, const std::filesystem::path& templatePath = {}); void ExecuteFrame(float deltaTime, float elapsedTime) override; diff --git a/src/services/impl/frame_workflow_step_registrar.cpp b/src/services/impl/frame_workflow_step_registrar.cpp index 3c6b8ec..448105a 100644 --- a/src/services/impl/frame_workflow_step_registrar.cpp +++ b/src/services/impl/frame_workflow_step_registrar.cpp @@ -8,6 +8,7 @@ #include "workflow_frame_render_step.hpp" #include "workflow_frame_scene_step.hpp" #include "workflow_step_registry.hpp" +#include "workflow_validation_checkpoint_step.hpp" #include #include @@ -19,13 +20,15 @@ FrameWorkflowStepRegistrar::FrameWorkflowStepRegistrar(std::shared_ptr std::shared_ptr inputService, std::shared_ptr physicsService, std::shared_ptr sceneService, - std::shared_ptr renderService) + std::shared_ptr renderService, + std::shared_ptr validationTourService) : logger_(std::move(logger)), audioService_(std::move(audioService)), inputService_(std::move(inputService)), physicsService_(std::move(physicsService)), sceneService_(std::move(sceneService)), - renderService_(std::move(renderService)) {} + renderService_(std::move(renderService)), + validationTourService_(std::move(validationTourService)) {} void FrameWorkflowStepRegistrar::RegisterUsedSteps( const WorkflowDefinition& workflow, @@ -59,6 +62,10 @@ void FrameWorkflowStepRegistrar::RegisterUsedSteps( if (plugins.contains("frame.gui")) { registry->RegisterStep(std::make_shared(inputService_, logger_)); } + if (plugins.contains("validation.tour.checkpoint")) { + registry->RegisterStep(std::make_shared( + validationTourService_, logger_)); + } } } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/frame_workflow_step_registrar.hpp b/src/services/impl/frame_workflow_step_registrar.hpp index 523606c..75752cd 100644 --- a/src/services/impl/frame_workflow_step_registrar.hpp +++ b/src/services/impl/frame_workflow_step_registrar.hpp @@ -12,6 +12,7 @@ class IInputService; class IPhysicsService; class IRenderCoordinatorService; class ISceneService; +class IValidationTourService; } namespace sdl3cpp::services::impl { @@ -23,7 +24,8 @@ public: std::shared_ptr inputService, std::shared_ptr physicsService, std::shared_ptr sceneService, - std::shared_ptr renderService); + std::shared_ptr renderService, + std::shared_ptr validationTourService); void RegisterUsedSteps(const WorkflowDefinition& workflow, const std::shared_ptr& registry) const; @@ -35,6 +37,7 @@ private: std::shared_ptr physicsService_; std::shared_ptr sceneService_; std::shared_ptr renderService_; + std::shared_ptr validationTourService_; }; } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/validation_tour_service.cpp b/src/services/impl/validation_tour_service.cpp index 8bdeab5..46cb206 100644 --- a/src/services/impl/validation_tour_service.cpp +++ b/src/services/impl/validation_tour_service.cpp @@ -29,6 +29,14 @@ ValidationTourService::ValidationTourService(std::shared_ptr con if (configService) { config_ = configService->GetValidationTourConfig(); } + checkpointIndexById_.clear(); + for (size_t i = 0; i < config_.checkpoints.size(); ++i) { + const auto& checkpoint = config_.checkpoints[i]; + if (checkpoint.id.empty()) { + continue; + } + checkpointIndexById_.emplace(checkpoint.id, i); + } if (config_.enabled && config_.checkpoints.empty()) { if (logger_) { @@ -66,6 +74,43 @@ ValidationTourService::ValidationTourService(std::shared_ptr con AdvanceCheckpoint(); } +bool ValidationTourService::RequestCheckpoint(const std::string& checkpointId) { + if (!config_.enabled) { + if (logger_) { + logger_->Trace("ValidationTourService", "RequestCheckpoint", + "checkpoint=" + checkpointId, + "Validation tour disabled"); + } + return true; + } + if (checkpointId.empty()) { + if (logger_) { + logger_->Error("ValidationTourService::RequestCheckpoint: checkpoint id is empty"); + } + return false; + } + auto it = checkpointIndexById_.find(checkpointId); + if (it == checkpointIndexById_.end()) { + if (logger_) { + logger_->Error("ValidationTourService::RequestCheckpoint: checkpoint not found id=" + checkpointId); + } + return false; + } + checkpointIndex_ = it->second; + pendingCapture_.reset(); + failed_ = false; + completed_ = false; + active_ = true; + AdvanceCheckpoint(); + if (logger_) { + logger_->Trace("ValidationTourService", "RequestCheckpoint", + "checkpoint=" + checkpointId + + ", index=" + std::to_string(checkpointIndex_), + "Checkpoint requested"); + } + return true; +} + ValidationFramePlan ValidationTourService::BeginFrame(float aspect) { ValidationFramePlan plan{}; if (!IsActive()) { diff --git a/src/services/impl/validation_tour_service.hpp b/src/services/impl/validation_tour_service.hpp index e79d4eb..8763685 100644 --- a/src/services/impl/validation_tour_service.hpp +++ b/src/services/impl/validation_tour_service.hpp @@ -6,6 +6,7 @@ #include "../interfaces/i_validation_tour_service.hpp" #include #include +#include namespace sdl3cpp::services::impl { @@ -15,6 +16,7 @@ public: std::shared_ptr probeService, std::shared_ptr logger); + bool RequestCheckpoint(const std::string& checkpointId) override; ValidationFramePlan BeginFrame(float aspect) override; ValidationFrameResult EndFrame() override; bool IsActive() const override { return active_ && !completed_ && !failed_; } @@ -47,6 +49,7 @@ private: std::shared_ptr probeService_; std::shared_ptr logger_; ValidationTourConfig config_{}; + std::unordered_map checkpointIndexById_{}; std::filesystem::path resolvedOutputDir_{}; size_t checkpointIndex_ = 0; uint32_t warmupRemaining_ = 0; diff --git a/src/services/impl/workflow_validation_checkpoint_step.cpp b/src/services/impl/workflow_validation_checkpoint_step.cpp new file mode 100644 index 0000000..74b029e --- /dev/null +++ b/src/services/impl/workflow_validation_checkpoint_step.cpp @@ -0,0 +1,42 @@ +#include "workflow_validation_checkpoint_step.hpp" +#include "workflow_step_io_resolver.hpp" + +#include + +namespace sdl3cpp::services::impl { + +WorkflowValidationCheckpointStep::WorkflowValidationCheckpointStep( + std::shared_ptr validationService, + std::shared_ptr logger) + : validationService_(std::move(validationService)), + logger_(std::move(logger)) {} + +std::string WorkflowValidationCheckpointStep::GetPluginId() const { + return "validation.tour.checkpoint"; +} + +void WorkflowValidationCheckpointStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) { + if (!validationService_) { + throw std::runtime_error("validation.tour.checkpoint requires an IValidationTourService"); + } + WorkflowStepIoResolver resolver; + const std::string checkpointKey = resolver.GetRequiredInputKey(step, "checkpoint"); + const auto* checkpointValue = context.TryGet(checkpointKey); + const std::string checkpointId = checkpointValue ? *checkpointValue : checkpointKey; + const bool fromContext = checkpointValue != nullptr; + if (checkpointId.empty()) { + throw std::runtime_error("validation.tour.checkpoint missing checkpoint id"); + } + if (!validationService_->RequestCheckpoint(checkpointId)) { + throw std::runtime_error("validation.tour.checkpoint checkpoint not found: " + checkpointId); + } + if (logger_) { + logger_->Trace("WorkflowValidationCheckpointStep", "Execute", + "checkpoint=" + checkpointId + + ", source=" + std::string(fromContext ? "context" : "literal") + + ", active=" + std::string(validationService_->IsActive() ? "true" : "false"), + "Checkpoint scheduled"); + } +} + +} // namespace sdl3cpp::services::impl diff --git a/src/services/impl/workflow_validation_checkpoint_step.hpp b/src/services/impl/workflow_validation_checkpoint_step.hpp new file mode 100644 index 0000000..98831c9 --- /dev/null +++ b/src/services/impl/workflow_validation_checkpoint_step.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "../interfaces/i_logger.hpp" +#include "../interfaces/i_validation_tour_service.hpp" +#include "../interfaces/i_workflow_step.hpp" + +#include +#include + +namespace sdl3cpp::services::impl { + +class WorkflowValidationCheckpointStep final : public IWorkflowStep { +public: + WorkflowValidationCheckpointStep(std::shared_ptr validationService, + std::shared_ptr logger); + + std::string GetPluginId() const override; + void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override; + +private: + std::shared_ptr validationService_; + std::shared_ptr logger_; +}; + +} // namespace sdl3cpp::services::impl diff --git a/src/services/interfaces/i_validation_tour_service.hpp b/src/services/interfaces/i_validation_tour_service.hpp index 6b5a111..92c9067 100644 --- a/src/services/interfaces/i_validation_tour_service.hpp +++ b/src/services/interfaces/i_validation_tour_service.hpp @@ -26,6 +26,14 @@ class IValidationTourService { public: virtual ~IValidationTourService() = default; + /** + * @brief Request a specific checkpoint by id. + * + * @param checkpointId Checkpoint identifier from validation_tour config + * @return true if the checkpoint was accepted or validation is disabled + */ + virtual bool RequestCheckpoint(const std::string& checkpointId) = 0; + /** * @brief Prepare validation state for the upcoming frame. *