Add step 607 operational SLO watch

This commit is contained in:
Bill
2026-02-17 12:00:27 -07:00
parent 293138306d
commit b0cabe583d
4 changed files with 253 additions and 0 deletions

View File

@@ -4342,4 +4342,13 @@ target_link_libraries(step606_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step607_test tests/step607_test.cpp)
target_include_directories(step607_test PRIVATE src)
target_link_libraries(step607_test PRIVATE
nlohmann_json::nlohmann_json
unofficial::tree-sitter::tree-sitter
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)

View File

@@ -0,0 +1,64 @@
#pragma once
// Step 607: Operational SLO Watch
#include <map>
#include <string>
#include <vector>
#include "ValidationErrorUtil.h"
struct SLOIndicator {
std::string indicatorId;
std::string serviceId;
int targetPercent = 0;
int observedPercent = 0;
int errorBudgetBurn = 0;
};
class OperationalSLOWatch {
public:
bool upsert(const SLOIndicator& indicator, std::string* error) {
if (!error) return false;
error->clear();
if (indicator.indicatorId.empty()) return failWith(error, "indicator_id_missing");
if (indicator.serviceId.empty()) return failWith(error, "service_id_missing");
if (indicator.targetPercent < 0 || indicator.targetPercent > 100) return failWith(error, "target_percent_invalid");
if (indicator.observedPercent < 0 || indicator.observedPercent > 100) return failWith(error, "observed_percent_invalid");
if (indicator.errorBudgetBurn < 0) return failWith(error, "error_budget_burn_invalid");
if (items_.count(indicator.indicatorId) == 0) order_.push_back(indicator.indicatorId);
items_[indicator.indicatorId] = indicator;
return true;
}
bool breaching(const std::string& indicatorId, std::string* error) const {
if (!error) return false;
error->clear();
auto it = items_.find(indicatorId);
if (it == items_.end()) return failWith(error, "indicator_missing");
const auto& i = it->second;
return i.observedPercent < i.targetPercent || i.errorBudgetBurn > 100;
}
int serviceBreachCount(const std::string& serviceId) const {
int count = 0;
for (const auto& id : order_) {
const auto& i = items_.at(id);
if (i.serviceId == serviceId &&
(i.observedPercent < i.targetPercent || i.errorBudgetBurn > 100)) ++count;
}
return count;
}
std::vector<SLOIndicator> byService(const std::string& serviceId) const {
std::vector<SLOIndicator> out;
for (const auto& id : order_) {
const auto& i = items_.at(id);
if (serviceId.empty() || i.serviceId == serviceId) out.push_back(i);
}
return out;
}
private:
std::map<std::string, SLOIndicator> items_;
std::vector<std::string> order_;
};

View File

@@ -0,0 +1,150 @@
// Step 607: Operational SLO Watch (12 tests)
#include "OperationalSLOWatch.h"
#include <iostream>
static int passed = 0, failed = 0;
#define TEST(name) { std::cout << " " << #name << "... "; }
#define PASS() { std::cout << "PASS\n"; ++passed; }
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
static SLOIndicator indicator(const std::string& id,
const std::string& service,
int target,
int observed,
int burn) {
return {id, service, target, observed, burn};
}
void test_upsert_success() {
TEST(upsert_success);
OperationalSLOWatch watch;
std::string error;
CHECK(watch.upsert(indicator("i1", "svc-a", 99, 99, 50), &error), "upsert should succeed");
CHECK(watch.byService("svc-a").size() == 1, "service count mismatch");
PASS();
}
void test_upsert_rejects_missing_indicator_id() {
TEST(upsert_rejects_missing_indicator_id);
OperationalSLOWatch watch;
std::string error;
CHECK(!watch.upsert(indicator("", "svc-a", 99, 99, 50), &error), "upsert should fail");
CHECK(error == "indicator_id_missing", "wrong error");
PASS();
}
void test_upsert_rejects_missing_service_id() {
TEST(upsert_rejects_missing_service_id);
OperationalSLOWatch watch;
std::string error;
CHECK(!watch.upsert(indicator("i1", "", 99, 99, 50), &error), "upsert should fail");
CHECK(error == "service_id_missing", "wrong error");
PASS();
}
void test_upsert_rejects_invalid_target_percent() {
TEST(upsert_rejects_invalid_target_percent);
OperationalSLOWatch watch;
std::string error;
CHECK(!watch.upsert(indicator("i1", "svc-a", 120, 99, 50), &error), "upsert should fail");
CHECK(error == "target_percent_invalid", "wrong error");
PASS();
}
void test_upsert_rejects_invalid_observed_percent() {
TEST(upsert_rejects_invalid_observed_percent);
OperationalSLOWatch watch;
std::string error;
CHECK(!watch.upsert(indicator("i1", "svc-a", 99, -1, 50), &error), "upsert should fail");
CHECK(error == "observed_percent_invalid", "wrong error");
PASS();
}
void test_upsert_rejects_negative_error_budget_burn() {
TEST(upsert_rejects_negative_error_budget_burn);
OperationalSLOWatch watch;
std::string error;
CHECK(!watch.upsert(indicator("i1", "svc-a", 99, 99, -1), &error), "upsert should fail");
CHECK(error == "error_budget_burn_invalid", "wrong error");
PASS();
}
void test_breaching_true_when_observed_below_target() {
TEST(breaching_true_when_observed_below_target);
OperationalSLOWatch watch;
std::string error;
CHECK(watch.upsert(indicator("i1", "svc-a", 99, 98, 20), &error), "upsert failed");
CHECK(watch.breaching("i1", &error), "indicator should breach");
PASS();
}
void test_breaching_true_when_burn_exceeds_hundred() {
TEST(breaching_true_when_burn_exceeds_hundred);
OperationalSLOWatch watch;
std::string error;
CHECK(watch.upsert(indicator("i1", "svc-a", 99, 100, 101), &error), "upsert failed");
CHECK(watch.breaching("i1", &error), "indicator should breach");
PASS();
}
void test_breaching_false_when_healthy() {
TEST(breaching_false_when_healthy);
OperationalSLOWatch watch;
std::string error;
CHECK(watch.upsert(indicator("i1", "svc-a", 99, 100, 40), &error), "upsert failed");
CHECK(!watch.breaching("i1", &error), "indicator should be healthy");
PASS();
}
void test_breaching_rejects_missing_indicator() {
TEST(breaching_rejects_missing_indicator);
OperationalSLOWatch watch;
std::string error;
CHECK(!watch.breaching("missing", &error), "breach should fail");
CHECK(error == "indicator_missing", "wrong error");
PASS();
}
void test_service_breach_count_counts_only_breaches() {
TEST(service_breach_count_counts_only_breaches);
OperationalSLOWatch watch;
std::string error;
CHECK(watch.upsert(indicator("i1", "svc-a", 99, 98, 10), &error), "upsert i1 failed");
CHECK(watch.upsert(indicator("i2", "svc-a", 99, 100, 10), &error), "upsert i2 failed");
CHECK(watch.upsert(indicator("i3", "svc-a", 99, 100, 110), &error), "upsert i3 failed");
CHECK(watch.serviceBreachCount("svc-a") == 2, "breach count mismatch");
PASS();
}
void test_by_service_empty_returns_all() {
TEST(by_service_empty_returns_all);
OperationalSLOWatch watch;
std::string error;
CHECK(watch.upsert(indicator("i1", "svc-a", 99, 99, 10), &error), "upsert i1 failed");
CHECK(watch.upsert(indicator("i2", "svc-b", 99, 99, 10), &error), "upsert i2 failed");
CHECK(watch.byService("").size() == 2, "empty service should return all");
PASS();
}
int main() {
std::cout << "Step 607: Operational SLO Watch\n";
test_upsert_success(); // 1
test_upsert_rejects_missing_indicator_id(); // 2
test_upsert_rejects_missing_service_id(); // 3
test_upsert_rejects_invalid_target_percent(); // 4
test_upsert_rejects_invalid_observed_percent(); // 5
test_upsert_rejects_negative_error_budget_burn(); // 6
test_breaching_true_when_observed_below_target(); // 7
test_breaching_true_when_burn_exceeds_hundred(); // 8
test_breaching_false_when_healthy(); // 9
test_breaching_rejects_missing_indicator(); // 10
test_service_breach_count_counts_only_breaches(); // 11
test_by_service_empty_returns_all(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -11800,3 +11800,33 @@ recovery-time metrics.
- `editor/src/RollbackDrillTracker.h` within header-size limit (`93` <= `600`)
- `editor/tests/step606_test.cpp` within test-file size guidance (`153` lines)
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
### Step 607: Operational SLO Watch
**Status:** PASS (12/12 tests)
Implements operational SLO indicator tracking with breach detection and
service-level breach aggregation.
**Files added:**
- `editor/src/OperationalSLOWatch.h` - SLO watch module:
- indicator upsert with identifier/range validation
- breach detection for target miss or budget over-burn
- service breach counts and service filtering helpers
- `editor/tests/step607_test.cpp` - 12 tests covering:
- upsert success/failure validation behavior
- breach decision behavior and missing indicator handling
- service breach aggregation and filter behavior
**Files modified:**
- `editor/CMakeLists.txt` - `step607_test` target
**Verification run:**
- `cmake -S editor -B editor/build-native` - PASS
- `cmake --build editor/build-native --target step607_test step606_test` - PASS
- `./editor/build-native/step607_test` - PASS (12/12)
- `./editor/build-native/step606_test` - PASS (12/12) regression coverage
**Architecture gate check:**
- `editor/src/OperationalSLOWatch.h` within header-size limit (`64` <= `600`)
- `editor/tests/step607_test.cpp` within test-file size guidance (`150` lines)
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`