From cc1136f698699a698a51055844b7b79faec4170b Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 17 Feb 2026 11:27:22 -0700 Subject: [PATCH] Step 590: add crash recovery reliability sweep --- editor/CMakeLists.txt | 9 + editor/src/CrashRecoveryReliabilitySweep.h | 73 ++++++++ editor/tests/step590_test.cpp | 186 +++++++++++++++++++++ progress.md | 33 ++++ 4 files changed, 301 insertions(+) create mode 100644 editor/src/CrashRecoveryReliabilitySweep.h create mode 100644 editor/tests/step590_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 2f0a2dc..4f02e7a 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -4189,4 +4189,13 @@ target_link_libraries(step589_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step590_test tests/step590_test.cpp) +target_include_directories(step590_test PRIVATE src) +target_link_libraries(step590_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) diff --git a/editor/src/CrashRecoveryReliabilitySweep.h b/editor/src/CrashRecoveryReliabilitySweep.h new file mode 100644 index 0000000..329e6ed --- /dev/null +++ b/editor/src/CrashRecoveryReliabilitySweep.h @@ -0,0 +1,73 @@ +#pragma once +// Step 590: Crash + Recovery Reliability Sweep + +#include +#include +#include + +struct RecoverySessionSnapshot { + std::string sessionId; + std::string workflowId; + int checkpoint = 0; + std::vector openBuffers; + std::vector queuedTaskIds; +}; + +struct RecoveryResult { + bool recovered = false; + bool workflowContinuity = false; + bool queueContinuity = false; + bool bufferContinuity = false; + std::vector notes; +}; + +class CrashRecoveryReliabilitySweep { +public: + bool captureSnapshot(const RecoverySessionSnapshot& snapshot, std::string* error) { + if (!error) return false; + error->clear(); + if (snapshot.sessionId.empty()) return fail(error, "session_id_missing"); + if (snapshot.workflowId.empty()) return fail(error, "workflow_id_missing"); + if (snapshot.checkpoint <= 0) return fail(error, "checkpoint_invalid"); + snapshots_[snapshot.sessionId] = snapshot; + return true; + } + + RecoveryResult recover(const std::string& sessionId) const { + RecoveryResult result; + auto it = snapshots_.find(sessionId); + if (it == snapshots_.end()) { + result.notes.push_back("recover:session_missing"); + return result; + } + const auto& s = it->second; + result.workflowContinuity = !s.workflowId.empty() && s.checkpoint > 0; + result.queueContinuity = !s.queuedTaskIds.empty(); + result.bufferContinuity = !s.openBuffers.empty(); + result.recovered = result.workflowContinuity && result.queueContinuity && result.bufferContinuity; + + if (result.recovered) { + result.notes.push_back("recover:ok"); + } else { + result.notes.push_back("recover:partial"); + if (!result.workflowContinuity) result.notes.push_back("recover:workflow_missing"); + if (!result.queueContinuity) result.notes.push_back("recover:queue_missing"); + if (!result.bufferContinuity) result.notes.push_back("recover:buffers_missing"); + } + return result; + } + + std::vector knownSessions() const { + std::vector ids; + for (const auto& kv : snapshots_) ids.push_back(kv.first); + return ids; + } + +private: + std::map snapshots_; + + static bool fail(std::string* error, const char* code) { + *error = code; + return false; + } +}; diff --git a/editor/tests/step590_test.cpp b/editor/tests/step590_test.cpp new file mode 100644 index 0000000..3fdde8c --- /dev/null +++ b/editor/tests/step590_test.cpp @@ -0,0 +1,186 @@ +// Step 590: Crash + Recovery Reliability Sweep (12 tests) + +#include "CrashRecoveryReliabilitySweep.h" + +#include + +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 RecoverySessionSnapshot snap(const std::string& sessionId) { + RecoverySessionSnapshot s; + s.sessionId = sessionId; + s.workflowId = "wf-1"; + s.checkpoint = 3; + s.openBuffers = {"a.cpp"}; + s.queuedTaskIds = {"task-1"}; + return s; +} + +static bool hasNote(const RecoveryResult& r, const std::string& note) { + for (const auto& n : r.notes) if (n == note) return true; + return false; +} + +void test_capture_snapshot_success() { + TEST(capture_snapshot_success); + CrashRecoveryReliabilitySweep sweep; + std::string error; + CHECK(sweep.captureSnapshot(snap("s1"), &error), "capture should succeed"); + CHECK(sweep.knownSessions().size() == 1, "session should be stored"); + PASS(); +} + +void test_capture_snapshot_rejects_missing_session() { + TEST(capture_snapshot_rejects_missing_session); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap(""); + CHECK(!sweep.captureSnapshot(s, &error), "capture should fail"); + CHECK(error == "session_id_missing", "wrong error"); + PASS(); +} + +void test_capture_snapshot_rejects_missing_workflow() { + TEST(capture_snapshot_rejects_missing_workflow); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap("s1"); + s.workflowId.clear(); + CHECK(!sweep.captureSnapshot(s, &error), "capture should fail"); + CHECK(error == "workflow_id_missing", "wrong error"); + PASS(); +} + +void test_capture_snapshot_rejects_invalid_checkpoint() { + TEST(capture_snapshot_rejects_invalid_checkpoint); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap("s1"); + s.checkpoint = 0; + CHECK(!sweep.captureSnapshot(s, &error), "capture should fail"); + CHECK(error == "checkpoint_invalid", "wrong error"); + PASS(); +} + +void test_recover_success_when_all_continuity_present() { + TEST(recover_success_when_all_continuity_present); + CrashRecoveryReliabilitySweep sweep; + std::string error; + CHECK(sweep.captureSnapshot(snap("s1"), &error), "capture failed"); + auto r = sweep.recover("s1"); + CHECK(r.recovered, "recovery should be complete"); + CHECK(r.workflowContinuity && r.queueContinuity && r.bufferContinuity, "all continuity flags expected"); + CHECK(hasNote(r, "recover:ok"), "ok note expected"); + PASS(); +} + +void test_recover_missing_session_returns_note() { + TEST(recover_missing_session_returns_note); + CrashRecoveryReliabilitySweep sweep; + auto r = sweep.recover("missing"); + CHECK(!r.recovered, "recovery should fail"); + CHECK(hasNote(r, "recover:session_missing"), "missing session note expected"); + PASS(); +} + +void test_recover_partial_when_queue_missing() { + TEST(recover_partial_when_queue_missing); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap("s1"); + s.queuedTaskIds.clear(); + CHECK(sweep.captureSnapshot(s, &error), "capture failed"); + auto r = sweep.recover("s1"); + CHECK(!r.recovered, "recovery should be partial"); + CHECK(!r.queueContinuity, "queue continuity should fail"); + CHECK(hasNote(r, "recover:queue_missing"), "queue missing note expected"); + PASS(); +} + +void test_recover_partial_when_buffers_missing() { + TEST(recover_partial_when_buffers_missing); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap("s1"); + s.openBuffers.clear(); + CHECK(sweep.captureSnapshot(s, &error), "capture failed"); + auto r = sweep.recover("s1"); + CHECK(!r.recovered, "recovery should be partial"); + CHECK(!r.bufferContinuity, "buffer continuity should fail"); + CHECK(hasNote(r, "recover:buffers_missing"), "buffer missing note expected"); + PASS(); +} + +void test_recover_partial_when_workflow_invalid() { + TEST(recover_partial_when_workflow_invalid); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap("s1"); + s.workflowId = "wf-1"; + s.checkpoint = -1; + s.queuedTaskIds = {"task-1"}; + s.openBuffers = {"a.cpp"}; + CHECK(!sweep.captureSnapshot(s, &error), "capture should fail"); + CHECK(error == "checkpoint_invalid", "wrong checkpoint error"); + PASS(); +} + +void test_known_sessions_returns_all_ids() { + TEST(known_sessions_returns_all_ids); + CrashRecoveryReliabilitySweep sweep; + std::string error; + CHECK(sweep.captureSnapshot(snap("s1"), &error), "capture s1 failed"); + CHECK(sweep.captureSnapshot(snap("s2"), &error), "capture s2 failed"); + auto ids = sweep.knownSessions(); + CHECK(ids.size() == 2, "two ids expected"); + PASS(); +} + +void test_capture_overwrites_existing_session() { + TEST(capture_overwrites_existing_session); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap("s1"); + CHECK(sweep.captureSnapshot(s, &error), "capture 1 failed"); + s.openBuffers = {"b.cpp"}; + CHECK(sweep.captureSnapshot(s, &error), "capture 2 failed"); + auto r = sweep.recover("s1"); + CHECK(r.bufferContinuity, "buffer continuity should remain true"); + PASS(); +} + +void test_partial_recovery_has_partial_note() { + TEST(partial_recovery_has_partial_note); + CrashRecoveryReliabilitySweep sweep; + std::string error; + auto s = snap("s1"); + s.openBuffers.clear(); + CHECK(sweep.captureSnapshot(s, &error), "capture failed"); + auto r = sweep.recover("s1"); + CHECK(hasNote(r, "recover:partial"), "partial note expected"); + PASS(); +} + +int main() { + std::cout << "Step 590: Crash + Recovery Reliability Sweep\n"; + + test_capture_snapshot_success(); // 1 + test_capture_snapshot_rejects_missing_session(); // 2 + test_capture_snapshot_rejects_missing_workflow(); // 3 + test_capture_snapshot_rejects_invalid_checkpoint();// 4 + test_recover_success_when_all_continuity_present();// 5 + test_recover_missing_session_returns_note(); // 6 + test_recover_partial_when_queue_missing(); // 7 + test_recover_partial_when_buffers_missing(); // 8 + test_recover_partial_when_workflow_invalid(); // 9 + test_known_sessions_returns_all_ids(); // 10 + test_capture_overwrites_existing_session(); // 11 + test_partial_recovery_has_partial_note(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 6c3a424..308adc0 100644 --- a/progress.md +++ b/progress.md @@ -11160,3 +11160,36 @@ regression, telemetry, and documentation readiness. - `editor/src/ReleaseReadinessGatePack.h` within header-size limit (`55` <= `600`) - `editor/tests/step589_test.cpp` within test-file size guidance (`154` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 590: Crash + Recovery Reliability Sweep +**Status:** PASS (12/12 tests) + +Implements crash/recovery reliability modeling for session snapshots and +continuity checks across workflow, queue, and open-buffer state. + +**Files added:** +- `editor/src/CrashRecoveryReliabilitySweep.h` - crash recovery module: + - recovery snapshot capture with validation guards + - session restore evaluation with continuity dimensions + - partial-recovery diagnostic notes + - known session inventory helper +- `editor/tests/step590_test.cpp` - 12 tests covering: + - snapshot capture success/failure behavior + - full recovery behavior + - partial recovery dimensions and notes behavior + - missing-session behavior + - snapshot overwrite and session inventory behavior + +**Files modified:** +- `editor/CMakeLists.txt` - `step590_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step590_test step589_test` - PASS +- `./editor/build-native/step590_test` - PASS (12/12) +- `./editor/build-native/step589_test` - PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/CrashRecoveryReliabilitySweep.h` within header-size limit (`73` <= `600`) +- `editor/tests/step590_test.cpp` within test-file size guidance (`186` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`