Step 592: add documentation operator playbooks
This commit is contained in:
@@ -4207,4 +4207,13 @@ target_link_libraries(step591_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step592_test tests/step592_test.cpp)
|
||||
target_include_directories(step592_test PRIVATE src)
|
||||
target_link_libraries(step592_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)
|
||||
|
||||
99
editor/src/DocumentationOperatorPlaybooks.h
Normal file
99
editor/src/DocumentationOperatorPlaybooks.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
// Step 592: Documentation + Operator Playbooks
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct PlaybookDoc {
|
||||
std::string docId;
|
||||
std::string title;
|
||||
std::vector<std::string> sections;
|
||||
std::vector<std::string> operatorChecks;
|
||||
};
|
||||
|
||||
class DocumentationOperatorPlaybooks {
|
||||
public:
|
||||
bool addDocument(const std::string& docId,
|
||||
const std::string& title,
|
||||
std::string* error) {
|
||||
if (!error) return false;
|
||||
error->clear();
|
||||
if (docId.empty()) return fail(error, "doc_id_missing");
|
||||
if (title.empty()) return fail(error, "doc_title_missing");
|
||||
if (docs_.count(docId) != 0) return fail(error, "doc_duplicate");
|
||||
docs_[docId] = PlaybookDoc{docId, title, {}, {}};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addSection(const std::string& docId,
|
||||
const std::string& section,
|
||||
std::string* error) {
|
||||
if (!error) return false;
|
||||
error->clear();
|
||||
auto* doc = find(docId);
|
||||
if (!doc) return fail(error, "doc_missing");
|
||||
if (section.empty()) return fail(error, "section_missing");
|
||||
for (const auto& s : doc->sections) if (s == section) return fail(error, "section_duplicate");
|
||||
doc->sections.push_back(section);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addOperatorCheck(const std::string& docId,
|
||||
const std::string& check,
|
||||
std::string* error) {
|
||||
if (!error) return false;
|
||||
error->clear();
|
||||
auto* doc = find(docId);
|
||||
if (!doc) return fail(error, "doc_missing");
|
||||
if (check.empty()) return fail(error, "check_missing");
|
||||
for (const auto& c : doc->operatorChecks) if (c == check) return fail(error, "check_duplicate");
|
||||
doc->operatorChecks.push_back(check);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isDeploymentReady(const std::string& docId) const {
|
||||
const auto* doc = findConst(docId);
|
||||
if (!doc) return false;
|
||||
return !doc->sections.empty() && !doc->operatorChecks.empty();
|
||||
}
|
||||
|
||||
int coverageScore(const std::string& docId) const {
|
||||
const auto* doc = findConst(docId);
|
||||
if (!doc) return 0;
|
||||
int score = 0;
|
||||
score += std::min(60, static_cast<int>(doc->sections.size()) * 15);
|
||||
score += std::min(40, static_cast<int>(doc->operatorChecks.size()) * 10);
|
||||
return score;
|
||||
}
|
||||
|
||||
std::vector<PlaybookDoc> allDocuments() const {
|
||||
std::vector<PlaybookDoc> out;
|
||||
for (const auto& kv : docs_) out.push_back(kv.second);
|
||||
std::stable_sort(out.begin(), out.end(), [](const PlaybookDoc& a, const PlaybookDoc& b) {
|
||||
return a.docId < b.docId;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, PlaybookDoc> docs_;
|
||||
|
||||
static bool fail(std::string* error, const char* code) {
|
||||
*error = code;
|
||||
return false;
|
||||
}
|
||||
|
||||
PlaybookDoc* find(const std::string& docId) {
|
||||
auto it = docs_.find(docId);
|
||||
if (it == docs_.end()) return nullptr;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
const PlaybookDoc* findConst(const std::string& docId) const {
|
||||
auto it = docs_.find(docId);
|
||||
if (it == docs_.end()) return nullptr;
|
||||
return &it->second;
|
||||
}
|
||||
};
|
||||
154
editor/tests/step592_test.cpp
Normal file
154
editor/tests/step592_test.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// Step 592: Documentation + Operator Playbooks (12 tests)
|
||||
|
||||
#include "DocumentationOperatorPlaybooks.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 {}
|
||||
|
||||
void test_add_document_success() {
|
||||
TEST(add_document_success);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "add doc should succeed");
|
||||
CHECK(p.allDocuments().size() == 1, "doc count mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_document_rejects_missing_id() {
|
||||
TEST(add_document_rejects_missing_id);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(!p.addDocument("", "Deployment Guide", &error), "add doc should fail");
|
||||
CHECK(error == "doc_id_missing", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_document_rejects_duplicate() {
|
||||
TEST(add_document_rejects_duplicate);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "first add failed");
|
||||
CHECK(!p.addDocument("doc-1", "Duplicate", &error), "duplicate should fail");
|
||||
CHECK(error == "doc_duplicate", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_section_success() {
|
||||
TEST(add_section_success);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "add doc failed");
|
||||
CHECK(p.addSection("doc-1", "Install", &error), "add section failed");
|
||||
CHECK(p.allDocuments()[0].sections.size() == 1, "section count mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_section_rejects_missing_doc() {
|
||||
TEST(add_section_rejects_missing_doc);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(!p.addSection("missing", "Install", &error), "add section should fail");
|
||||
CHECK(error == "doc_missing", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_section_rejects_duplicate() {
|
||||
TEST(add_section_rejects_duplicate);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "add doc failed");
|
||||
CHECK(p.addSection("doc-1", "Install", &error), "add section failed");
|
||||
CHECK(!p.addSection("doc-1", "Install", &error), "duplicate section should fail");
|
||||
CHECK(error == "section_duplicate", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_operator_check_success() {
|
||||
TEST(add_operator_check_success);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "add doc failed");
|
||||
CHECK(p.addOperatorCheck("doc-1", "Verify logs clean", &error), "add check failed");
|
||||
CHECK(p.allDocuments()[0].operatorChecks.size() == 1, "check count mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_operator_check_rejects_duplicate() {
|
||||
TEST(add_operator_check_rejects_duplicate);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "add doc failed");
|
||||
CHECK(p.addOperatorCheck("doc-1", "Verify logs clean", &error), "add check failed");
|
||||
CHECK(!p.addOperatorCheck("doc-1", "Verify logs clean", &error), "duplicate check should fail");
|
||||
CHECK(error == "check_duplicate", "wrong error");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_is_deployment_ready_requires_sections_and_checks() {
|
||||
TEST(is_deployment_ready_requires_sections_and_checks);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "add doc failed");
|
||||
CHECK(!p.isDeploymentReady("doc-1"), "should not be ready yet");
|
||||
CHECK(p.addSection("doc-1", "Install", &error), "add section failed");
|
||||
CHECK(!p.isDeploymentReady("doc-1"), "should still not be ready");
|
||||
CHECK(p.addOperatorCheck("doc-1", "Verify logs clean", &error), "add check failed");
|
||||
CHECK(p.isDeploymentReady("doc-1"), "should be ready");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_coverage_score_combines_sections_and_checks() {
|
||||
TEST(coverage_score_combines_sections_and_checks);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-1", "Deployment Guide", &error), "add doc failed");
|
||||
CHECK(p.addSection("doc-1", "Install", &error), "add section failed");
|
||||
CHECK(p.addSection("doc-1", "Troubleshooting", &error), "add section failed");
|
||||
CHECK(p.addOperatorCheck("doc-1", "Verify logs clean", &error), "add check failed");
|
||||
CHECK(p.coverageScore("doc-1") == 40, "coverage score mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_all_documents_sorted_by_doc_id() {
|
||||
TEST(all_documents_sorted_by_doc_id);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
std::string error;
|
||||
CHECK(p.addDocument("doc-b", "B", &error), "add doc-b failed");
|
||||
CHECK(p.addDocument("doc-a", "A", &error), "add doc-a failed");
|
||||
auto docs = p.allDocuments();
|
||||
CHECK(docs[0].docId == "doc-a", "sorted order mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_unknown_doc_readiness_and_score_default() {
|
||||
TEST(unknown_doc_readiness_and_score_default);
|
||||
DocumentationOperatorPlaybooks p;
|
||||
CHECK(!p.isDeploymentReady("missing"), "missing doc should not be ready");
|
||||
CHECK(p.coverageScore("missing") == 0, "missing doc score should be zero");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 592: Documentation + Operator Playbooks\n";
|
||||
|
||||
test_add_document_success(); // 1
|
||||
test_add_document_rejects_missing_id(); // 2
|
||||
test_add_document_rejects_duplicate(); // 3
|
||||
test_add_section_success(); // 4
|
||||
test_add_section_rejects_missing_doc(); // 5
|
||||
test_add_section_rejects_duplicate(); // 6
|
||||
test_add_operator_check_success(); // 7
|
||||
test_add_operator_check_rejects_duplicate(); // 8
|
||||
test_is_deployment_ready_requires_sections_and_checks(); // 9
|
||||
test_coverage_score_combines_sections_and_checks();// 10
|
||||
test_all_documents_sorted_by_doc_id(); // 11
|
||||
test_unknown_doc_readiness_and_score_default(); // 12
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
34
progress.md
34
progress.md
@@ -11226,3 +11226,37 @@ efficiency tracking, aggregation, and baseline-vs-candidate comparison.
|
||||
- `editor/src/BenchmarkComparisonHarness.h` within header-size limit (`109` <= `600`)
|
||||
- `editor/tests/step591_test.cpp` within test-file size guidance (`165` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
### Step 592: Documentation + Operator Playbooks
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Implements documentation/playbook tracking for deployment and support workflows,
|
||||
including section/check coverage and readiness scoring.
|
||||
|
||||
**Files added:**
|
||||
- `editor/src/DocumentationOperatorPlaybooks.h` - doc/playbook module:
|
||||
- document registration and dedupe guards
|
||||
- section and operator-check management
|
||||
- deployment readiness evaluation
|
||||
- coverage scoring helper
|
||||
- sorted document inventory helper
|
||||
- `editor/tests/step592_test.cpp` - 12 tests covering:
|
||||
- document/section/check add success/failure behavior
|
||||
- dedupe guard behavior
|
||||
- readiness gating behavior
|
||||
- coverage scoring behavior
|
||||
- sorted inventory and unknown-doc fallback behavior
|
||||
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` - `step592_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `cmake -S editor -B editor/build-native` - PASS
|
||||
- `cmake --build editor/build-native --target step592_test step591_test` - PASS
|
||||
- `./editor/build-native/step592_test` - PASS (12/12)
|
||||
- `./editor/build-native/step591_test` - PASS (12/12) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/DocumentationOperatorPlaybooks.h` within header-size limit (`99` <= `600`)
|
||||
- `editor/tests/step592_test.cpp` within test-file size guidance (`154` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
Reference in New Issue
Block a user