From 2711cf16585794f2469245654a1c35128e68a375 Mon Sep 17 00:00:00 2001 From: scawful Date: Tue, 30 Dec 2025 11:24:15 -0500 Subject: [PATCH] studio: fix data roots and refine datasets --- apps/studio/README.md | 3 +- apps/studio/src/core/registry_reader.cc | 29 +++- apps/studio/src/data_loader.cc | 129 +++++++++++++----- apps/studio/src/main.cc | 10 +- apps/studio/src/models/state.h | 2 + apps/studio/src/ui/components/charts.cc | 8 +- .../src/ui/components/companion_panels.cc | 6 +- .../studio/src/ui/components/graph_browser.cc | 7 +- apps/studio/src/ui/components/panels.cc | 124 ++++++++++++----- apps/studio/src/ui/core.cc | 7 +- apps/studio/src/widgets/sample_review.cc | 25 +++- apps/studio/src/widgets/training_status.cpp | 11 +- 12 files changed, 268 insertions(+), 93 deletions(-) diff --git a/apps/studio/README.md b/apps/studio/README.md index 9705e93..afd0ccf 100644 --- a/apps/studio/README.md +++ b/apps/studio/README.md @@ -18,9 +18,10 @@ cmake --build build --target afs_studio ## Data sources -- Training data path: `~/src/training` if present, otherwise `~/.context/training` (override with CLI arg). +- Training data path: `AFS_TRAINING_ROOT` if set, otherwise `~/src/training` or `~/.context/training` (override with CLI arg). - Context graph: `AFS_GRAPH_PATH` or `${AFS_CONTEXT_ROOT}/index/afs_graph.json` (defaults to `~/src/context` or `~/.context`). - Dataset registry: `AFS_DATASET_REGISTRY` or `${AFS_TRAINING_ROOT}/index/dataset_registry.json`. +- Resource index: `AFS_RESOURCE_INDEX` or `${AFS_TRAINING_ROOT}/index/resource_index.json`. ## Features diff --git a/apps/studio/src/core/registry_reader.cc b/apps/studio/src/core/registry_reader.cc index 2551af6..ee43368 100644 --- a/apps/studio/src/core/registry_reader.cc +++ b/apps/studio/src/core/registry_reader.cc @@ -2,8 +2,35 @@ #include "logger.h" #include "filesystem.h" +#include #include +namespace { + +std::filesystem::path ResolveContextRoot() { + const char* env_root = std::getenv("AFS_CONTEXT_ROOT"); + if (env_root && env_root[0] != '\0') { + auto path = afs::studio::core::FileSystem::ResolvePath(env_root); + if (afs::studio::core::FileSystem::Exists(path)) { + return path; + } + } + + auto preferred = afs::studio::core::FileSystem::ResolvePath("~/src/context"); + if (afs::studio::core::FileSystem::Exists(preferred)) { + return preferred; + } + + auto fallback = afs::studio::core::FileSystem::ResolvePath("~/.context"); + if (afs::studio::core::FileSystem::Exists(fallback)) { + return fallback; + } + + return preferred; +} + +} // namespace + namespace afs { namespace studio { @@ -13,7 +40,7 @@ RegistryReader::RegistryReader(const std::filesystem::path& registry_path) : registry_path_(registry_path) {} std::filesystem::path RegistryReader::ResolveDefaultPath() const { - return core::FileSystem::ResolvePath("~/.context/models/registry.json"); + return ResolveContextRoot() / "models" / "registry.json"; } bool RegistryReader::Exists() const { diff --git a/apps/studio/src/data_loader.cc b/apps/studio/src/data_loader.cc index a686271..e583eeb 100644 --- a/apps/studio/src/data_loader.cc +++ b/apps/studio/src/data_loader.cc @@ -36,9 +36,12 @@ std::optional ResolveHafsScawfulRoot() { return plugin_path; } - auto legacy_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful"); - if (studio::core::FileSystem::Exists(legacy_path)) { - return legacy_path; + auto trunk_root = ResolveTrunkRoot(); + if (trunk_root) { + auto candidate = *trunk_root / "scawful" / "research" / "afs_scawful"; + if (studio::core::FileSystem::Exists(candidate)) { + return candidate; + } } return std::nullopt; @@ -121,6 +124,57 @@ std::filesystem::path ResolveDatasetRegistryPath() { return ResolveTrainingRoot() / "index" / "dataset_registry.json"; } +std::filesystem::path ResolveResourceIndexPath(const std::string& data_root, + const DataLoader::PathExists& exists) { + const char* env_path = std::getenv("AFS_RESOURCE_INDEX"); + if (env_path && env_path[0] != '\0') { + return studio::core::FileSystem::ResolvePath(env_path); + } + + std::vector candidates; + auto training_root = ResolveTrainingRoot(); + if (!training_root.empty()) { + candidates.push_back(training_root / "index" / "resource_index.json"); + candidates.push_back(training_root / "resource_index.json"); + } + if (!data_root.empty()) { + auto data_path = std::filesystem::path(data_root); + candidates.push_back(data_path / "index" / "resource_index.json"); + candidates.push_back(data_path / "resource_index.json"); + } + + for (const auto& candidate : candidates) { + if (exists(candidate.string())) { + return candidate; + } + } + return {}; +} + +std::filesystem::path ResolveTrainingDataPath(const std::string& filename, + const std::string& data_root, + const DataLoader::PathExists& exists) { + std::vector candidates; + if (!data_root.empty()) { + auto data_path = std::filesystem::path(data_root); + candidates.push_back(data_path / filename); + candidates.push_back(data_path / "index" / filename); + } + + auto training_root = ResolveTrainingRoot(); + if (!training_root.empty()) { + candidates.push_back(training_root / filename); + candidates.push_back(training_root / "index" / filename); + } + + for (const auto& candidate : candidates) { + if (exists(candidate.string())) { + return candidate; + } + } + return {}; +} + constexpr float kTrendDeltaThreshold = 0.05f; bool IsWhitespaceOnly(const std::string& s) { @@ -168,14 +222,20 @@ bool DataLoader::Refresh() { last_error_.clear(); last_status_ = LoadStatus{}; - if (!path_exists_(data_path_)) { + const bool base_exists = !data_path_.empty() && path_exists_(data_path_); + const auto training_root = ResolveTrainingRoot(); + const bool training_exists = !training_root.empty() && path_exists_(training_root.string()); + if (!base_exists && !training_exists) { last_error_ = "Data path does not exist: " + data_path_; LOG_ERROR(last_error_); last_status_.error_count = 1; last_status_.last_error = last_error_; last_status_.last_error_source = "data_path"; } else { - LOG_INFO("DataLoader: Refreshing from " + data_path_); + const auto& root = base_exists ? data_path_ : training_root.string(); + if (!root.empty()) { + LOG_INFO("DataLoader: Refreshing from " + root); + } } auto next_quality_trends = quality_trends_; @@ -338,17 +398,16 @@ DataLoader::LoadResult DataLoader::LoadQualityFeedback( RejectionSummary* rejection_summary) { LoadResult result; - std::string path = data_path_ + "/quality_feedback.json"; - if (!path_exists_(path)) { - LOG_WARN("quality_feedback.json not found at " + path); - return result; + auto path = ResolveTrainingDataPath("quality_feedback.json", data_path_, path_exists_); + if (path.empty()) { + return result; } - LOG_INFO("DataLoader: Loading " + path); + LOG_INFO("DataLoader: Loading " + path.string()); result.found = true; std::string content; std::string read_error; - if (!file_reader_(path, &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { + if (!file_reader_(path.string(), &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { result.ok = false; result.error = read_error.empty() ? "quality_feedback.json is empty" : read_error; return result; @@ -456,14 +515,14 @@ DataLoader::LoadResult DataLoader::LoadActiveLearning( CoverageData* coverage) { LoadResult result; - std::string path = data_path_ + "/active_learning.json"; - if (!path_exists_(path)) return result; + auto path = ResolveTrainingDataPath("active_learning.json", data_path_, path_exists_); + if (path.empty()) return result; - LOG_INFO("DataLoader: Loading " + path); + LOG_INFO("DataLoader: Loading " + path.string()); result.found = true; std::string content; std::string read_error; - if (!file_reader_(path, &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { + if (!file_reader_(path.string(), &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { result.ok = false; result.error = read_error.empty() ? "active_learning.json is empty" : read_error; return result; @@ -508,14 +567,14 @@ DataLoader::LoadResult DataLoader::LoadTrainingFeedback( OptimizationData* optimization_data) { LoadResult result; - std::string path = data_path_ + "/training_feedback.json"; - if (!path_exists_(path)) return result; + auto path = ResolveTrainingDataPath("training_feedback.json", data_path_, path_exists_); + if (path.empty()) return result; - LOG_INFO("DataLoader: Loading " + path); + LOG_INFO("DataLoader: Loading " + path.string()); result.found = true; std::string content; std::string read_error; - if (!file_reader_(path, &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { + if (!file_reader_(path.string(), &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { result.ok = false; result.error = read_error.empty() ? "training_feedback.json is empty" : read_error; return result; @@ -574,17 +633,16 @@ DataLoader::LoadResult DataLoader::LoadTrainingFeedback( DataLoader::LoadResult DataLoader::LoadCuratedHacks( std::vector* curated_hacks) { LoadResult result; - std::string path = data_path_ + "/curated_hacks.json"; - if (!path_exists_(path)) { - LOG_WARN("curated_hacks.json not found at " + path); + auto path = ResolveTrainingDataPath("curated_hacks.json", data_path_, path_exists_); + if (path.empty()) { return result; } - LOG_INFO("DataLoader: Loading " + path); + LOG_INFO("DataLoader: Loading " + path.string()); result.found = true; std::string content; std::string read_error; - if (!file_reader_(path, &content, &read_error) || content.empty() || + if (!file_reader_(path.string(), &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { result.ok = false; result.error = @@ -644,17 +702,16 @@ DataLoader::LoadResult DataLoader::LoadCuratedHacks( DataLoader::LoadResult DataLoader::LoadResourceIndex(ResourceIndexData* resource_index) { LoadResult result; - std::string path = data_path_ + "/resource_index.json"; - if (!path_exists_(path)) { - LOG_WARN("resource_index.json not found at " + path); + auto path = ResolveResourceIndexPath(data_path_, path_exists_); + if (path.empty()) { return result; } - LOG_INFO("DataLoader: Loading " + path); + LOG_INFO("DataLoader: Loading " + path.string()); result.found = true; std::string content; std::string read_error; - if (!file_reader_(path, &content, &read_error) || content.empty() || + if (!file_reader_(path.string(), &content, &read_error) || content.empty() || IsWhitespaceOnly(content)) { result.ok = false; result.error = read_error.empty() ? "resource_index.json is empty" : read_error; @@ -843,9 +900,17 @@ DataLoader::LoadResult DataLoader::LoadContextGraph(ContextGraphData* context_gr void DataLoader::MountDrive(const std::string& name) { auto scawful_root = ResolveHafsScawfulRoot(); - std::filesystem::path script_path = scawful_root - ? *scawful_root / "scripts" / "mount_windows.sh" - : studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful/scripts/mount_windows.sh"); + std::filesystem::path script_path; + if (scawful_root) { + script_path = *scawful_root / "scripts" / "mount_windows.sh"; + } else { + auto trunk_root = ResolveTrunkRoot(); + if (trunk_root) { + script_path = *trunk_root / "scawful" / "research" / "afs_scawful" / "scripts" / "mount_windows.sh"; + } else { + script_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful/scripts/mount_windows.sh"); + } + } if (studio::core::FileSystem::Exists(script_path)) { LOG_INFO("DataLoader: Triggering mount using " + script_path.string()); diff --git a/apps/studio/src/main.cc b/apps/studio/src/main.cc index 9587c96..f336f4d 100644 --- a/apps/studio/src/main.cc +++ b/apps/studio/src/main.cc @@ -12,6 +12,7 @@ /// Ctrl+Q - Quit /// Ctrl+/ - Shortcut editor +#include #include #include @@ -27,8 +28,13 @@ int main(int argc, char* argv[]) { if (argc > 1) { data_path_str = argv[1]; } else { - auto preferred = FileSystem::ResolvePath("~/src/training"); - data_path_str = FileSystem::Exists(preferred) ? preferred.string() : "~/.context/training"; + const char* env_root = std::getenv("AFS_TRAINING_ROOT"); + if (env_root && env_root[0] != '\0') { + data_path_str = env_root; + } else { + auto preferred = FileSystem::ResolvePath("~/src/training"); + data_path_str = FileSystem::Exists(preferred) ? preferred.string() : "~/.context/training"; + } } std::filesystem::path data_path = FileSystem::ResolvePath(data_path_str); diff --git a/apps/studio/src/models/state.h b/apps/studio/src/models/state.h index 0f0bd5a..92d7a88 100644 --- a/apps/studio/src/models/state.h +++ b/apps/studio/src/models/state.h @@ -151,6 +151,7 @@ struct AppState { std::vector compared_run_ids; int selected_generator_index = -1; std::string selected_generator_name; + int selected_dataset_index = -1; PlotKind focus_plot = PlotKind::None; std::map domain_visibility; Workspace current_workspace = Workspace::Dashboard; @@ -206,6 +207,7 @@ struct AppState { std::array log_filter{}; std::array run_filter{}; std::array generator_filter{}; + std::array dataset_filter{}; std::array chat_input{}; std::array system_prompt{}; std::array user_prompt{}; diff --git a/apps/studio/src/ui/components/charts.cc b/apps/studio/src/ui/components/charts.cc index 0394c68..0ac3041 100644 --- a/apps/studio/src/ui/components/charts.cc +++ b/apps/studio/src/ui/components/charts.cc @@ -862,8 +862,8 @@ void RenderEmbeddingQualityChart(AppState& state, const DataLoader& loader) { void RenderAgentThroughputChart(AppState& state, const DataLoader& loader) { RenderChartHeader(PlotKind::AgentThroughput, - "AGENT THROUGHPUT", - "Real-time task processing rate across the swarm. Higher peaks indicate high-availability periods; the dashed line represents the Swarm Target (1.0k).", + "AGENT WORKLOAD", + "Tasks completed and queue depth per agent from the current data snapshot.", state); if (state.agents.empty()) { @@ -953,8 +953,8 @@ void RenderMissionQueueChart(AppState& state, const DataLoader& loader) { void RenderLatentSpaceChart(AppState& state, const DataLoader& loader) { RenderChartHeader(PlotKind::LatentSpace, - "LATENT TOPOLOGY", - "Visualization of the manifold learned by the model. Clusters indicate stable concept representations; voids represent potential logic gaps.", + "EMBEDDING MAP", + "Synthetic layout based on embedding region index; use for rough clustering only.", state); const auto& regions = loader.GetEmbeddingRegions(); diff --git a/apps/studio/src/ui/components/companion_panels.cc b/apps/studio/src/ui/components/companion_panels.cc index 1aacd8d..8ed27fc 100644 --- a/apps/studio/src/ui/components/companion_panels.cc +++ b/apps/studio/src/ui/components/companion_panels.cc @@ -37,15 +37,12 @@ CompanionPanels::PanelVisibility CompanionPanels::GetPanelVisibility(PlotKind ki vis.controls = true; break; - case PlotKind::AgentUtilization: case PlotKind::AgentThroughput: case PlotKind::MountsStatus: vis.data_quality = true; vis.inspector = true; break; - case PlotKind::MissionProgress: - case PlotKind::MissionQueue: case PlotKind::KnowledgeGraph: vis.controls = true; break; @@ -255,9 +252,10 @@ void CompanionPanels::RenderInspectorPanel(AppState& state, const DataLoader& lo case PlotKind::EmbeddingQuality: graph_name = "Embedding Quality"; break; case PlotKind::Rejections: graph_name = "Rejections"; break; case PlotKind::EvalMetrics: graph_name = "Eval Metrics"; break; - case PlotKind::AgentUtilization: graph_name = "Agent Utilization"; break; + case PlotKind::AgentThroughput: graph_name = "Agent Workload"; break; case PlotKind::MountsStatus: graph_name = "Mounts Status"; break; case PlotKind::KnowledgeGraph: graph_name = "Context Graph"; break; + case PlotKind::LatentSpace: graph_name = "Embedding Map"; break; default: break; } ImGui::Text("Graph: %s", graph_name); diff --git a/apps/studio/src/ui/components/graph_browser.cc b/apps/studio/src/ui/components/graph_browser.cc index ba91404..8789e19 100644 --- a/apps/studio/src/ui/components/graph_browser.cc +++ b/apps/studio/src/ui/components/graph_browser.cc @@ -23,10 +23,7 @@ void GraphBrowser::InitializeGraphRegistry() { {PlotKind::Effectiveness, "Effectiveness", "Generator effectiveness analysis", GraphCategory::Quality, false, true, true, false}, // System Category - {PlotKind::AgentUtilization, "Agent Utilization", "Agent resource usage and activity", GraphCategory::System, false, false, true, true}, - {PlotKind::AgentThroughput, "Agent Throughput", "Agent task completion rates", GraphCategory::System, false, false, true, true}, - {PlotKind::MissionProgress, "Mission Progress", "Mission completion tracking", GraphCategory::System, false, false, false, false}, - {PlotKind::MissionQueue, "Mission Queue", "Mission queue depth over time", GraphCategory::System, false, false, false, false}, + {PlotKind::AgentThroughput, "Agent Workload", "Tasks completed and queue depth per agent", GraphCategory::System, false, false, true, true}, {PlotKind::MountsStatus, "Mounts Status", "Filesystem mount status", GraphCategory::System, false, false, true, false}, // Coverage Category @@ -35,7 +32,7 @@ void GraphBrowser::InitializeGraphRegistry() { // Embedding Category {PlotKind::EmbeddingDensity, "Embedding Density", "Embedding space density visualization", GraphCategory::Embedding, false, true, true, true}, - {PlotKind::LatentSpace, "Latent Space", "2D latent space projection", GraphCategory::Embedding, false, true, true, true}, + {PlotKind::LatentSpace, "Embedding Map", "Synthetic layout from embedding regions", GraphCategory::Embedding, false, true, true, true}, {PlotKind::KnowledgeGraph, "Context Graph", "AFS context and mount relationships", GraphCategory::Embedding, false, false, false, false}, // Optimization Category diff --git a/apps/studio/src/ui/components/panels.cc b/apps/studio/src/ui/components/panels.cc index 1a04724..953fb6d 100644 --- a/apps/studio/src/ui/components/panels.cc +++ b/apps/studio/src/ui/components/panels.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -33,9 +34,18 @@ std::filesystem::path ResolveHafsScawfulRoot() { return plugin_path; } - auto legacy_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful"); - if (studio::core::FileSystem::Exists(legacy_path)) { - return legacy_path; + const char* trunk_env = std::getenv("TRUNK_ROOT"); + if (trunk_env && trunk_env[0] != '\0') { + auto trunk_root = studio::core::FileSystem::ResolvePath(trunk_env); + auto candidate = trunk_root / "scawful" / "research" / "afs_scawful"; + if (studio::core::FileSystem::Exists(candidate)) { + return candidate; + } + } + + auto fallback_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful"); + if (studio::core::FileSystem::Exists(fallback_path)) { + return fallback_path; } return {}; @@ -219,41 +229,24 @@ static void WriteStringArray(std::ostringstream& output, const std::string& key, output << "]\n"; } -static bool RunCuratedSummaryBuild(const std::filesystem::path& script_path, std::string* output) { +static bool RunPythonScript(const std::filesystem::path& script_path, + const std::filesystem::path& module_root, + std::string* output) { if (output) output->clear(); if (!studio::core::FileSystem::Exists(script_path)) { - if (output) *output = "Summary script not found"; + if (output) *output = "Script not found"; return false; } - std::string cmd = "python3 \"" + script_path.string() + "\" 2>&1"; + std::string cmd; + if (!module_root.empty()) { + cmd = "AFS_SCAWFUL_ROOT=\"" + module_root.string() + "\" "; + cmd += "PYTHONPATH=\"" + module_root.string() + "\" "; + } + cmd += "python3 \"" + script_path.string() + "\" 2>&1"; FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) { - if (output) *output = "Failed to launch summary build"; - return false; - } - - char buffer[256]; - std::ostringstream result; - while (fgets(buffer, sizeof(buffer), pipe)) { - result << buffer; - } - int status = pclose(pipe); - if (output) *output = result.str(); - return status == 0; -} - -static bool RunResourceIndexBuild(const std::filesystem::path& script_path, std::string* output) { - if (output) output->clear(); - if (!studio::core::FileSystem::Exists(script_path)) { - if (output) *output = "Resource index script not found"; - return false; - } - - std::string cmd = "python3 \"" + script_path.string() + "\" 2>&1"; - FILE* pipe = popen(cmd.c_str(), "r"); - if (!pipe) { - if (output) *output = "Failed to launch resource index build"; + if (output) *output = "Failed to launch script"; return false; } @@ -565,7 +558,7 @@ void RenderDatasetPanel(AppState& state, const DataLoader& loader) { ? std::filesystem::current_path() / "rebuild_resource_index.py" : scawful_root / "scripts" / "rebuild_resource_index.py"; std::string build_output; - bool ok = RunResourceIndexBuild(script_path, &build_output); + bool ok = RunPythonScript(script_path, scawful_root, &build_output); if (!build_output.empty()) { resource_status = ok ? "Resource index rebuilt (see logs)" : "Resource index rebuild failed (see logs)"; } else { @@ -656,13 +649,45 @@ void RenderDatasetPanel(AppState& state, const DataLoader& loader) { } if (ImGui::BeginTabItem("Datasets")) { + static std::string dataset_status; if (!dataset_error.empty()) { ImGui::TextColored(ImVec4(0.9f, 0.5f, 0.2f, 1.0f), "%s", dataset_error.c_str()); } + ImGui::InputTextWithHint("##DatasetFilter", "Filter by dataset name", state.dataset_filter.data(), state.dataset_filter.size()); + ImGui::SameLine(); + if (ImGui::Button("Clear##DatasetFilter")) state.dataset_filter[0] = '\0'; + ImGui::SameLine(); + if (ImGui::Button("Rebuild Dataset Registry")) { + auto scawful_root = ResolveHafsScawfulRoot(); + std::filesystem::path script_path = scawful_root.empty() + ? std::filesystem::current_path() / "build_dataset_registry.py" + : scawful_root / "scripts" / "build_dataset_registry.py"; + std::string build_output; + bool ok = RunPythonScript(script_path, scawful_root, &build_output); + if (!build_output.empty()) { + dataset_status = ok ? "Dataset registry rebuilt (see logs)" : "Dataset registry rebuild failed (see logs)"; + } else { + dataset_status = ok ? "Dataset registry rebuilt" : "Dataset registry rebuild failed"; + } + state.should_refresh = true; + } + if (!dataset_status.empty()) { + ImGui::SameLine(); + ImGui::TextDisabled("%s", dataset_status.c_str()); + } + if (dataset_registry.datasets.empty()) { ImGui::TextDisabled("No dataset registry loaded."); } else { + std::uint64_t total_size = 0; + for (const auto& dataset : dataset_registry.datasets) { + total_size += dataset.size_bytes; + } + double total_mb = static_cast(total_size) / (1024.0 * 1024.0); + ImGui::Text("Datasets: %zu", dataset_registry.datasets.size()); + ImGui::SameLine(); + ImGui::TextDisabled("Total size: %.2f MB", total_mb); if (!dataset_registry.generated_at.empty()) { ImGui::TextDisabled("Generated at: %s", dataset_registry.generated_at.c_str()); } @@ -676,10 +701,17 @@ void RenderDatasetPanel(AppState& state, const DataLoader& loader) { ImGui::TableSetupColumn("Updated", ImGuiTableColumnFlags_WidthStretch, 1.1f); ImGui::TableHeadersRow(); - for (const auto& dataset : dataset_registry.datasets) { + for (size_t i = 0; i < dataset_registry.datasets.size(); ++i) { + const auto& dataset = dataset_registry.datasets[i]; + if (!ContainsInsensitive(dataset.name, state.dataset_filter.data())) { + continue; + } ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text("%s", dataset.name.c_str()); + bool selected = static_cast(i) == state.selected_dataset_index; + if (ImGui::Selectable(dataset.name.c_str(), selected, ImGuiSelectableFlags_SpanAllColumns)) { + state.selected_dataset_index = static_cast(i); + } if (ImGui::IsItemHovered() && !dataset.path.empty()) { ImGui::BeginTooltip(); ImGui::Text("%s", dataset.path.c_str()); @@ -695,6 +727,28 @@ void RenderDatasetPanel(AppState& state, const DataLoader& loader) { } ImGui::EndTable(); } + + ImGui::Separator(); + if (state.selected_dataset_index >= 0 && + state.selected_dataset_index < static_cast(dataset_registry.datasets.size())) { + const auto& selected = dataset_registry.datasets[state.selected_dataset_index]; + ImGui::TextDisabled("Selected Dataset"); + ImGui::Text("%s", selected.name.c_str()); + ImGui::TextDisabled("%s", selected.path.empty() ? "-" : selected.path.c_str()); + ImGui::Text("Files: %zu", selected.files.size()); + + ImGui::BeginChild("DatasetFiles", ImVec2(0, 120), true); + size_t max_files = 12; + for (size_t i = 0; i < selected.files.size() && i < max_files; ++i) { + ImGui::BulletText("%s", selected.files[i].c_str()); + } + if (selected.files.size() > max_files) { + ImGui::TextDisabled("... and %zu more", selected.files.size() - max_files); + } + ImGui::EndChild(); + } else { + ImGui::TextDisabled("Select a dataset to view details."); + } } ImGui::EndTabItem(); @@ -861,7 +915,7 @@ void RenderDatasetPanel(AppState& state, const DataLoader& loader) { std::filesystem::path script_path = override_path.parent_path().parent_path(); script_path /= "scripts/build_curated_hacks_summary.py"; std::string build_output; - bool ok = RunCuratedSummaryBuild(script_path, &build_output); + bool ok = RunPythonScript(script_path, scawful_root, &build_output); overrides_status = ok ? "Saved overrides and rebuilt summary" : "Saved overrides, rebuild failed"; if (!build_output.empty()) { overrides_status += " (see logs)"; diff --git a/apps/studio/src/ui/core.cc b/apps/studio/src/ui/core.cc index 25eccc9..ecb2905 100644 --- a/apps/studio/src/ui/core.cc +++ b/apps/studio/src/ui/core.cc @@ -234,17 +234,14 @@ const std::vector& PlotOptions() { {PlotKind::LossVsSamples, "Loss vs Samples"}, {PlotKind::DomainCoverage, "Domain Coverage"}, {PlotKind::EmbeddingQuality, "Embedding Quality"}, - {PlotKind::AgentThroughput, "Agent Throughput"}, - {PlotKind::MissionQueue, "Mission Queue"}, + {PlotKind::AgentThroughput, "Agent Workload"}, {PlotKind::QualityDirection, "Quality Direction"}, {PlotKind::GeneratorMix, "Generator Mix"}, {PlotKind::EmbeddingDensity, "Embedding Density"}, - {PlotKind::AgentUtilization, "Agent Utilization"}, - {PlotKind::MissionProgress, "Mission Progress"}, {PlotKind::EvalMetrics, "Eval Metrics"}, {PlotKind::Rejections, "Rejection Reasons"}, {PlotKind::KnowledgeGraph, "Context Graph"}, - {PlotKind::LatentSpace, "Latent Space"}, + {PlotKind::LatentSpace, "Embedding Map"}, {PlotKind::Effectiveness, "Domain Effectiveness"}, {PlotKind::Thresholds, "Threshold Sensitivity"}, {PlotKind::MountsStatus, "Local Mounts Status"}, diff --git a/apps/studio/src/widgets/sample_review.cc b/apps/studio/src/widgets/sample_review.cc index 34469ad..e66bd13 100644 --- a/apps/studio/src/widgets/sample_review.cc +++ b/apps/studio/src/widgets/sample_review.cc @@ -1,6 +1,7 @@ #include "sample_review.h" #include #include +#include #include #include #include @@ -12,6 +13,28 @@ namespace viz { namespace { +std::filesystem::path ResolveContextRoot() { + const char* env_root = std::getenv("AFS_CONTEXT_ROOT"); + if (env_root && env_root[0] != '\0') { + auto path = studio::core::FileSystem::ResolvePath(env_root); + if (studio::core::FileSystem::Exists(path)) { + return path; + } + } + + auto preferred = studio::core::FileSystem::ResolvePath("~/src/context"); + if (studio::core::FileSystem::Exists(preferred)) { + return preferred; + } + + auto fallback = studio::core::FileSystem::ResolvePath("~/.context"); + if (studio::core::FileSystem::Exists(fallback)) { + return fallback; + } + + return preferred; +} + TrainingSample ParseSampleLine(const std::string& line, bool is_rejected = false) { TrainingSample sample; sample.is_rejected = is_rejected; @@ -334,7 +357,7 @@ void SampleReviewWidget::LoadContextFiles() { context_files_.clear(); // Load user's ASM files from alttp disassembly - auto alttp_path = std::filesystem::path(std::getenv("HOME")) / ".context" / "knowledge" / "alttp"; + auto alttp_path = ResolveContextRoot() / "knowledge" / "alttp"; if (studio::core::FileSystem::Exists(alttp_path)) { try { diff --git a/apps/studio/src/widgets/training_status.cpp b/apps/studio/src/widgets/training_status.cpp index 9f59e4c..460e4bd 100644 --- a/apps/studio/src/widgets/training_status.cpp +++ b/apps/studio/src/widgets/training_status.cpp @@ -106,9 +106,14 @@ bool TrainingStatusWidget::FetchHealthData() { if (env_root && env_root[0] != '\0') { root = env_root; } else { - const char* home = std::getenv("HOME"); - if (home && home[0] != '\0') { - root = std::string(home) + "/src/trunk/scawful/research/afs"; + const char* trunk_root = std::getenv("TRUNK_ROOT"); + if (trunk_root && trunk_root[0] != '\0') { + root = std::string(trunk_root) + "/scawful/research/afs"; + } else { + const char* home = std::getenv("HOME"); + if (home && home[0] != '\0') { + root = std::string(home) + "/src/trunk/scawful/research/afs"; + } } }