From e5e24c750fef6d9fe86805af2c483e4c98cd6daa Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 17 Feb 2026 10:50:52 -0700 Subject: [PATCH] Step 568: integrate phase 31a memory reflection workflow --- editor/CMakeLists.txt | 9 ++ editor/src/Phase31aIntegration.h | 156 +++++++++++++++++++++++++++++++ editor/tests/step568_test.cpp | 116 +++++++++++++++++++++++ progress.md | 42 +++++++++ 4 files changed, 323 insertions(+) create mode 100644 editor/src/Phase31aIntegration.h create mode 100644 editor/tests/step568_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 2e81a26..9133ee9 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3991,4 +3991,13 @@ target_link_libraries(step567_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step568_test tests/step568_test.cpp) +target_include_directories(step568_test PRIVATE src) +target_link_libraries(step568_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/Phase31aIntegration.h b/editor/src/Phase31aIntegration.h new file mode 100644 index 0000000..450c0eb --- /dev/null +++ b/editor/src/Phase31aIntegration.h @@ -0,0 +1,156 @@ +#pragma once +// Step 568: Phase 31a Integration + +#include "AllocationOwnershipTraceHooks.h" +#include "CallStackFrameInspector.h" +#include "DebugSessionModel.h" +#include "LeakCorruptionSignalBridge.h" +#include "MemoryInspectorUiModel.h" +#include "MemorySnapshotModel.h" + +#include +#include +#include + +struct Phase31aInput { + std::string sessionId = "session-31a"; + std::string snapshotId = "snapshot-31a"; + std::string threadId = "thread-main"; + std::string targetId = "target-main"; + std::string bufferId = "buffer-main"; + std::string executablePath = "/tmp/whetstone-app"; + + std::string stackRegionId = "stack-region"; + std::string heapRegionId = "heap-region"; + std::string pointerText = "0x2008"; + + std::string allocationId = "alloc-main"; + std::string signalAllocationId = ""; + std::string initialOwner = "runtime"; + std::string transferredOwner = "inspector"; + + std::string frameFunction = "run"; + std::string frameFile = "src/main.ws"; + int frameLine = 42; + bool emitCorruptionSignal = false; + bool omitSignalMessage = false; +}; + +struct Phase31aResult { + bool snapshotReady = false; + bool inspectorReady = false; + bool traceReady = false; + bool signalBridgeReady = false; + bool stackCorrelationReady = false; + bool phase31aPass = false; + MemorySignalSeverity maxSeverity = MemorySignalSeverity::Info; + std::vector notes; +}; + +class Phase31aIntegration { +public: + static Phase31aResult run(const Phase31aInput& input) { + Phase31aResult result; + MemorySnapshot snapshot; + std::string error; + + DebugSessionModel debugSession; + if (!debugSession.start(input.targetId, input.bufferId, input.executablePath)) { + result.notes.push_back("fail:debug_session_start"); + return finalize(result); + } + + if (!MemorySnapshotModel::createSnapshot(input.snapshotId, + input.sessionId, + input.threadId, + 1, + &snapshot, + &error)) { + result.notes.push_back("fail:snapshot_create"); + return finalize(result); + } + if (!MemorySnapshotModel::addRegion(&snapshot, {input.stackRegionId, MemoryRegionKind::Stack, 0x1000, 0x100, false}, &error) || + !MemorySnapshotModel::addRegion(&snapshot, {input.heapRegionId, MemoryRegionKind::Heap, 0x2000, 0x100, false}, &error) || + !MemorySnapshotModel::addReference(&snapshot, {0x1010, 0x2008, "stack_ptr"}, &error)) { + result.notes.push_back("fail:snapshot_content"); + return finalize(result); + } + result.snapshotReady = true; + + MemoryValueNode root; + MemoryValueNode pointerNode; + if (!MemoryInspectorUiModel::createComposite("root", "Memory", MemoryValueKind::Struct, &root, &error) || + !MemoryInspectorUiModel::createLeaf("ptr", "head", MemoryValueKind::Pointer, input.pointerText, &pointerNode, &error) || + !MemoryInspectorUiModel::appendChild(&root, pointerNode, &error)) { + result.notes.push_back("fail:inspector_node_build"); + return finalize(result); + } + const auto rows = MemoryInspectorUiModel::collectVisibleRows(root, 8); + const MemoryRegionRecord* pointerRegion = nullptr; + if (rows.size() < 2 || + !MemoryInspectorUiModel::resolvePointerRegion(snapshot, pointerNode, &pointerRegion, &error) || + !pointerRegion) { + result.notes.push_back("fail:inspector_pointer_resolve"); + return finalize(result); + } + result.inspectorReady = true; + + AllocationOwnershipTraceHooks hooks; + if (!hooks.recordAllocation(input.sessionId, input.allocationId, 0x2008, 64, input.initialOwner, 10, &error) || + !hooks.transferOwnership(input.sessionId, input.allocationId, input.transferredOwner, 20, &error)) { + result.notes.push_back("fail:allocation_trace"); + return finalize(result); + } + result.traceReady = hooks.ownerFor(input.allocationId) == input.transferredOwner; + if (!result.traceReady) { + result.notes.push_back("fail:allocation_owner_mismatch"); + return finalize(result); + } + + LeakCorruptionSignalBridge bridge; + const std::string signalAllocId = input.signalAllocationId.empty() ? input.allocationId : input.signalAllocationId; + const std::string leakMessage = input.omitSignalMessage ? "" : "Potential leak near allocation"; + if (!bridge.ingestSignal({"sig-1", input.sessionId, signalAllocId, MemorySignalType::LeakSuspected, + leakMessage, 30}, &error)) { + result.notes.push_back("fail:signal_bridge_ingest"); + return finalize(result); + } + if (input.emitCorruptionSignal && + !bridge.ingestSignal({"sig-2", input.sessionId, signalAllocId, MemorySignalType::CorruptionConfirmed, + "Corruption confirmed near allocation", 31}, &error)) { + result.notes.push_back("fail:signal_bridge_corruption_ingest"); + return finalize(result); + } + result.signalBridgeReady = !bridge.diagnosticsForSession(input.sessionId).empty(); + result.maxSeverity = bridge.highestSeverityForSession(input.sessionId); + + FrameScope frame; + frame.frameIndex = 0; + frame.functionName = input.frameFunction; + frame.filePath = input.frameFile; + frame.line = input.frameLine; + frame.variables["ptr"] = input.pointerText; + const auto frameResult = CallStackFrameInspector::inspect({frame}, 0); + const auto vars = CallStackFrameInspector::variableNames(frame); + const bool hasPointerVar = std::find(vars.begin(), vars.end(), "ptr") != vars.end(); + result.stackCorrelationReady = frameResult.ok && hasPointerVar && debugSession.hasTargetForBuffer(input.bufferId); + if (!result.stackCorrelationReady) { + result.notes.push_back("fail:stack_correlation"); + return finalize(result); + } + + result.notes.push_back("phase31a:memory_reflection_cycle_ready"); + return finalize(result); + } + +private: + static Phase31aResult finalize(Phase31aResult result) { + result.phase31aPass = result.snapshotReady && + result.inspectorReady && + result.traceReady && + result.signalBridgeReady && + result.stackCorrelationReady; + if (!result.phase31aPass) result.notes.push_back("phase31a:blocked"); + return result; + } +}; diff --git a/editor/tests/step568_test.cpp b/editor/tests/step568_test.cpp new file mode 100644 index 0000000..1c87c1e --- /dev/null +++ b/editor/tests/step568_test.cpp @@ -0,0 +1,116 @@ +// Step 568: Phase 31a Integration (8 tests) + +#include "Phase31aIntegration.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 bool hasNote(const Phase31aResult& result, const std::string& note) { + for (const auto& n : result.notes) if (n == note) return true; + return false; +} + +void test_full_cycle_passes() { + TEST(full_cycle_passes); + Phase31aInput input; + const auto result = Phase31aIntegration::run(input); + CHECK(result.phase31aPass, "phase should pass"); + CHECK(result.snapshotReady, "snapshot should be ready"); + CHECK(result.inspectorReady, "inspector should be ready"); + CHECK(result.traceReady, "trace should be ready"); + CHECK(result.signalBridgeReady, "signal bridge should be ready"); + CHECK(result.stackCorrelationReady, "stack correlation should be ready"); + CHECK(hasNote(result, "phase31a:memory_reflection_cycle_ready"), "success note missing"); + PASS(); +} + +void test_debug_session_start_failure_blocks_phase() { + TEST(debug_session_start_failure_blocks_phase); + Phase31aInput input; + input.targetId.clear(); + const auto result = Phase31aIntegration::run(input); + CHECK(!result.phase31aPass, "phase should fail"); + CHECK(hasNote(result, "fail:debug_session_start"), "expected debug start failure"); + PASS(); +} + +void test_snapshot_creation_failure_blocks_phase() { + TEST(snapshot_creation_failure_blocks_phase); + Phase31aInput input; + input.snapshotId.clear(); + const auto result = Phase31aIntegration::run(input); + CHECK(!result.phase31aPass, "phase should fail"); + CHECK(hasNote(result, "fail:snapshot_create"), "expected snapshot creation failure"); + PASS(); +} + +void test_invalid_pointer_text_blocks_inspector_resolution() { + TEST(invalid_pointer_text_blocks_inspector_resolution); + Phase31aInput input; + input.pointerText = "invalid-pointer"; + const auto result = Phase31aIntegration::run(input); + CHECK(!result.phase31aPass, "phase should fail"); + CHECK(hasNote(result, "fail:inspector_pointer_resolve"), "expected pointer resolve failure"); + PASS(); +} + +void test_invalid_transfer_owner_blocks_trace() { + TEST(invalid_transfer_owner_blocks_trace); + Phase31aInput input; + input.transferredOwner.clear(); + const auto result = Phase31aIntegration::run(input); + CHECK(!result.phase31aPass, "phase should fail"); + CHECK(hasNote(result, "fail:allocation_trace"), "expected allocation trace failure"); + PASS(); +} + +void test_signal_bridge_failure_blocks_phase() { + TEST(signal_bridge_failure_blocks_phase); + Phase31aInput input; + input.omitSignalMessage = true; + const auto result = Phase31aIntegration::run(input); + CHECK(!result.phase31aPass, "phase should fail"); + CHECK(hasNote(result, "fail:signal_bridge_ingest"), "expected signal bridge failure"); + PASS(); +} + +void test_invalid_stack_frame_blocks_correlation() { + TEST(invalid_stack_frame_blocks_correlation); + Phase31aInput input; + input.frameLine = 0; + const auto result = Phase31aIntegration::run(input); + CHECK(!result.phase31aPass, "phase should fail"); + CHECK(hasNote(result, "fail:stack_correlation"), "expected stack correlation failure"); + PASS(); +} + +void test_corruption_signal_raises_max_severity() { + TEST(corruption_signal_raises_max_severity); + Phase31aInput input; + input.emitCorruptionSignal = true; + const auto result = Phase31aIntegration::run(input); + CHECK(result.phase31aPass, "phase should pass"); + CHECK(result.maxSeverity == MemorySignalSeverity::Critical, "max severity should be critical"); + PASS(); +} + +int main() { + std::cout << "Step 568: Phase 31a Integration\n"; + + test_full_cycle_passes(); // 1 + test_debug_session_start_failure_blocks_phase(); // 2 + test_snapshot_creation_failure_blocks_phase(); // 3 + test_invalid_pointer_text_blocks_inspector_resolution();// 4 + test_invalid_transfer_owner_blocks_trace(); // 5 + test_signal_bridge_failure_blocks_phase(); // 6 + test_invalid_stack_frame_blocks_correlation(); // 7 + test_corruption_signal_raises_max_severity(); // 8 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 31001ff..d040394 100644 --- a/progress.md +++ b/progress.md @@ -10294,3 +10294,45 @@ session-scoped diagnostics and pane routing for memory debug surfaces. - `editor/src/LeakCorruptionSignalBridge.h` within header-size limit (`154` <= `600`) - `editor/tests/step567_test.cpp` within test-file size guidance (`176` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 568: Phase 31a Integration +**Status:** PASS (8/8 tests) + +Integrates Steps 564-567 into one memory-reflection control flow that validates +snapshot capture, UI inspection, allocation tracing, signal diagnostics, and +stack/debug correlation gating. + +**Files added:** +- `editor/src/Phase31aIntegration.h` - Phase 31a integration module: + - end-to-end memory reflection cycle orchestration + - debug session + snapshot bootstrap and region/reference population + - inspector node/row build and pointer-region resolution + - allocation/ownership trace lifecycle validation + - leak/corruption signal bridge ingestion and severity capture + - stack frame correlation checks against active debug context +- `editor/tests/step568_test.cpp` - 8 tests covering: + - full happy-path cycle behavior + - debug start/snapshot failure handling + - pointer resolution failure handling + - allocation transfer failure handling + - signal bridge failure handling + - stack correlation failure handling + - corruption severity escalation behavior + +**Files modified:** +- `editor/CMakeLists.txt` - `step568_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step568_test step567_test` - PASS +- `./editor/build-native/step568_test` - PASS (8/8) +- `./editor/build-native/step567_test` - PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/Phase31aIntegration.h` within header-size limit (`156` <= `600`) +- `editor/tests/step568_test.cpp` within test-file size guidance (`116` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +**Phase 31a totals (564-568):** +- **Steps completed:** 5 +- **New tests in this phase plan:** 56/56 passing