Add step 613 sprint 35 operational readiness
This commit is contained in:
@@ -4396,4 +4396,13 @@ target_link_libraries(step612_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step613_test tests/step613_test.cpp)
|
||||
target_include_directories(step613_test PRIVATE src)
|
||||
target_link_libraries(step613_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)
|
||||
|
||||
54
editor/src/Sprint35OperationalReadiness.h
Normal file
54
editor/src/Sprint35OperationalReadiness.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
// Step 613: Sprint 35 Operational Readiness
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ValidationErrorUtil.h"
|
||||
|
||||
struct Sprint35ReadinessInput {
|
||||
int freezeViolations = 0;
|
||||
int highRiskDependencies = 0;
|
||||
int onCallCoverageGaps = 0;
|
||||
int blockedCanaries = 0;
|
||||
int incompletePostmortems = 0;
|
||||
};
|
||||
|
||||
class Sprint35OperationalReadiness {
|
||||
public:
|
||||
static bool validate(const Sprint35ReadinessInput& in, std::string* error) {
|
||||
if (!error) return false;
|
||||
error->clear();
|
||||
if (in.freezeViolations < 0) return failWith(error, "freeze_violations_invalid");
|
||||
if (in.highRiskDependencies < 0) return failWith(error, "high_risk_dependencies_invalid");
|
||||
if (in.onCallCoverageGaps < 0) return failWith(error, "oncall_gaps_invalid");
|
||||
if (in.blockedCanaries < 0) return failWith(error, "blocked_canaries_invalid");
|
||||
if (in.incompletePostmortems < 0) return failWith(error, "incomplete_postmortems_invalid");
|
||||
return true;
|
||||
}
|
||||
|
||||
static int readinessScore(const Sprint35ReadinessInput& in) {
|
||||
int score = 100;
|
||||
score -= in.freezeViolations * 12;
|
||||
score -= in.highRiskDependencies * 8;
|
||||
score -= in.onCallCoverageGaps;
|
||||
score -= in.blockedCanaries * 15;
|
||||
score -= in.incompletePostmortems * 10;
|
||||
return std::max(0, std::min(100, score));
|
||||
}
|
||||
|
||||
static std::vector<std::string> blockers(const Sprint35ReadinessInput& in) {
|
||||
std::vector<std::string> out;
|
||||
if (in.freezeViolations > 0) out.push_back("freeze_violations_present");
|
||||
if (in.highRiskDependencies > 2) out.push_back("dependency_risk_elevated");
|
||||
if (in.onCallCoverageGaps > 0) out.push_back("oncall_coverage_incomplete");
|
||||
if (in.blockedCanaries > 0) out.push_back("canary_promotions_blocked");
|
||||
if (in.incompletePostmortems > 0) out.push_back("postmortems_incomplete");
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool releaseAllowed(const Sprint35ReadinessInput& in) {
|
||||
return blockers(in).empty() && readinessScore(in) >= 90;
|
||||
}
|
||||
};
|
||||
134
editor/tests/step613_test.cpp
Normal file
134
editor/tests/step613_test.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
// Step 613: Sprint 35 Operational Readiness (12 tests)
|
||||
|
||||
#include "Sprint35OperationalReadiness.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 Sprint35ReadinessInput healthy() {
|
||||
return {0, 0, 0, 0, 0};
|
||||
}
|
||||
|
||||
void test_validate_success() {
|
||||
TEST(validate_success);
|
||||
std::string error;
|
||||
CHECK(Sprint35OperationalReadiness::validate(healthy(), &error), "validate should pass");
|
||||
CHECK(error.empty(), "error should be empty");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_validate_rejects_negative_freeze_violations() {
|
||||
TEST(validate_rejects_negative_freeze_violations);
|
||||
auto in = healthy();
|
||||
in.freezeViolations = -1;
|
||||
std::string error;
|
||||
CHECK(!Sprint35OperationalReadiness::validate(in, &error), "validate should fail");
|
||||
CHECK(error == "freeze_violations_invalid", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_validate_rejects_negative_high_risk_dependencies() {
|
||||
TEST(validate_rejects_negative_high_risk_dependencies);
|
||||
auto in = healthy();
|
||||
in.highRiskDependencies = -1;
|
||||
std::string error;
|
||||
CHECK(!Sprint35OperationalReadiness::validate(in, &error), "validate should fail");
|
||||
CHECK(error == "high_risk_dependencies_invalid", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_validate_rejects_negative_oncall_gaps() {
|
||||
TEST(validate_rejects_negative_oncall_gaps);
|
||||
auto in = healthy();
|
||||
in.onCallCoverageGaps = -1;
|
||||
std::string error;
|
||||
CHECK(!Sprint35OperationalReadiness::validate(in, &error), "validate should fail");
|
||||
CHECK(error == "oncall_gaps_invalid", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_validate_rejects_negative_blocked_canaries() {
|
||||
TEST(validate_rejects_negative_blocked_canaries);
|
||||
auto in = healthy();
|
||||
in.blockedCanaries = -1;
|
||||
std::string error;
|
||||
CHECK(!Sprint35OperationalReadiness::validate(in, &error), "validate should fail");
|
||||
CHECK(error == "blocked_canaries_invalid", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_validate_rejects_negative_incomplete_postmortems() {
|
||||
TEST(validate_rejects_negative_incomplete_postmortems);
|
||||
auto in = healthy();
|
||||
in.incompletePostmortems = -1;
|
||||
std::string error;
|
||||
CHECK(!Sprint35OperationalReadiness::validate(in, &error), "validate should fail");
|
||||
CHECK(error == "incomplete_postmortems_invalid", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_readiness_score_full_when_healthy() {
|
||||
TEST(readiness_score_full_when_healthy);
|
||||
CHECK(Sprint35OperationalReadiness::readinessScore(healthy()) == 100, "healthy score should be 100");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_readiness_score_decreases_with_risks() {
|
||||
TEST(readiness_score_decreases_with_risks);
|
||||
auto in = Sprint35ReadinessInput{1, 1, 4, 1, 1};
|
||||
CHECK(Sprint35OperationalReadiness::readinessScore(in) < 100, "score should be reduced");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_readiness_score_clamps_to_zero() {
|
||||
TEST(readiness_score_clamps_to_zero);
|
||||
auto in = Sprint35ReadinessInput{10, 10, 40, 10, 10};
|
||||
CHECK(Sprint35OperationalReadiness::readinessScore(in) == 0, "score should clamp to zero");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_blockers_include_expected_flags() {
|
||||
TEST(blockers_include_expected_flags);
|
||||
auto in = Sprint35ReadinessInput{1, 3, 1, 1, 1};
|
||||
CHECK(Sprint35OperationalReadiness::blockers(in).size() == 5, "all blockers should be present");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_release_allowed_true_when_clean() {
|
||||
TEST(release_allowed_true_when_clean);
|
||||
CHECK(Sprint35OperationalReadiness::releaseAllowed(healthy()), "healthy should allow release");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_release_allowed_false_with_blockers() {
|
||||
TEST(release_allowed_false_with_blockers);
|
||||
auto in = healthy();
|
||||
in.blockedCanaries = 1;
|
||||
CHECK(!Sprint35OperationalReadiness::releaseAllowed(in), "blocked canaries should block release");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 613: Sprint 35 Operational Readiness\n";
|
||||
|
||||
test_validate_success(); // 1
|
||||
test_validate_rejects_negative_freeze_violations(); // 2
|
||||
test_validate_rejects_negative_high_risk_dependencies();// 3
|
||||
test_validate_rejects_negative_oncall_gaps(); // 4
|
||||
test_validate_rejects_negative_blocked_canaries(); // 5
|
||||
test_validate_rejects_negative_incomplete_postmortems();// 6
|
||||
test_readiness_score_full_when_healthy(); // 7
|
||||
test_readiness_score_decreases_with_risks(); // 8
|
||||
test_readiness_score_clamps_to_zero(); // 9
|
||||
test_blockers_include_expected_flags(); // 10
|
||||
test_release_allowed_true_when_clean(); // 11
|
||||
test_release_allowed_false_with_blockers(); // 12
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
30
progress.md
30
progress.md
@@ -11980,3 +11980,33 @@ risk scoring and manual override support.
|
||||
- `editor/src/CanaryPromotionJudge.h` within header-size limit (`72` <= `600`)
|
||||
- `editor/tests/step612_test.cpp` within test-file size guidance (`149` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
### Step 613: Sprint 35 Operational Readiness
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Implements sprint-level operational readiness scoring and release gating over
|
||||
freeze, dependency, coverage, canary, and postmortem signals.
|
||||
|
||||
**Files added:**
|
||||
- `editor/src/Sprint35OperationalReadiness.h` - sprint readiness module:
|
||||
- readiness input validation for non-negative metrics
|
||||
- weighted readiness score with clamp
|
||||
- blocker derivation and release-allowed decision
|
||||
- `editor/tests/step613_test.cpp` - 12 tests covering:
|
||||
- validation success/failure behavior
|
||||
- score degradation and clamp behavior
|
||||
- blocker derivation and release gate behavior
|
||||
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` - `step613_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `cmake -S editor -B editor/build-native` - PASS
|
||||
- `cmake --build editor/build-native --target step613_test step612_test` - PASS
|
||||
- `./editor/build-native/step613_test` - PASS (12/12)
|
||||
- `./editor/build-native/step612_test` - PASS (12/12) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/Sprint35OperationalReadiness.h` within header-size limit (`54` <= `600`)
|
||||
- `editor/tests/step613_test.cpp` within test-file size guidance (`134` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
Reference in New Issue
Block a user