Step 528: integrate constrained taskitem execution gate
This commit is contained in:
@@ -3631,4 +3631,13 @@ target_link_libraries(step527_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step528_test tests/step528_test.cpp)
|
||||
target_include_directories(step528_test PRIVATE src)
|
||||
target_link_libraries(step528_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)
|
||||
|
||||
86
editor/src/ConstrainedTaskitemIntegration.h
Normal file
86
editor/src/ConstrainedTaskitemIntegration.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
// Step 528: Phase 27a Integration
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "ConstraintViolationDiagnostics.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct ConstrainedExecutionRequest {
|
||||
json taskitemJson;
|
||||
std::string nodeKind;
|
||||
std::string requestedOp;
|
||||
std::vector<std::string> requestedSymbols;
|
||||
std::vector<ScopedSymbol> visibleSymbols;
|
||||
};
|
||||
|
||||
struct ConstrainedExecutionResult {
|
||||
bool routed = false;
|
||||
bool executed = false;
|
||||
TaskitemContract contract;
|
||||
ScopeSnapshot scopeSnapshot;
|
||||
ConstraintDiagnosticPacket diagnostics;
|
||||
std::vector<std::string> schemaErrors;
|
||||
};
|
||||
|
||||
class ConstrainedTaskitemIntegration {
|
||||
public:
|
||||
static ConstrainedExecutionResult run(const ConstrainedExecutionRequest& request,
|
||||
const LegalOperationGraph& graph,
|
||||
const SymbolScopeExtractor& scopeExtractor) {
|
||||
ConstrainedExecutionResult result;
|
||||
|
||||
auto validation = TypedTaskitemContractSchema::parseAndValidate(request.taskitemJson);
|
||||
if (!validation.valid) {
|
||||
result.schemaErrors = validation.errors;
|
||||
result.diagnostics = schemaFailurePacket(validation.errors);
|
||||
return result;
|
||||
}
|
||||
|
||||
result.contract = validation.contract;
|
||||
std::set<std::string> forbidden(result.contract.forbiddenSymbols.begin(),
|
||||
result.contract.forbiddenSymbols.end());
|
||||
|
||||
result.scopeSnapshot = scopeExtractor.buildSnapshot(
|
||||
result.contract.language,
|
||||
request.nodeKind,
|
||||
request.visibleSymbols,
|
||||
forbidden);
|
||||
|
||||
ConstraintCheckInput checkInput;
|
||||
checkInput.contract = result.contract;
|
||||
checkInput.language = result.contract.language;
|
||||
checkInput.nodeKind = request.nodeKind;
|
||||
checkInput.requestedOp = request.requestedOp;
|
||||
checkInput.requestedSymbols = request.requestedSymbols;
|
||||
checkInput.scopeSnapshot = result.scopeSnapshot;
|
||||
|
||||
result.diagnostics = ConstraintViolationDiagnostics::evaluate(
|
||||
checkInput, graph, scopeExtractor);
|
||||
result.routed = result.diagnostics.ok;
|
||||
result.executed = result.diagnostics.ok;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static ConstraintDiagnosticPacket schemaFailurePacket(
|
||||
const std::vector<std::string>& errors) {
|
||||
ConstraintDiagnosticPacket packet;
|
||||
packet.ok = false;
|
||||
packet.recommendedAction = "escalate";
|
||||
for (const auto& error : errors) {
|
||||
packet.violations.push_back({
|
||||
"under_constrained_contract",
|
||||
"Taskitem contract failed schema validation",
|
||||
error,
|
||||
false
|
||||
});
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
};
|
||||
179
editor/tests/step528_test.cpp
Normal file
179
editor/tests/step528_test.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// Step 528: Phase 27a Integration (8 tests)
|
||||
|
||||
#include "ConstrainedTaskitemIntegration.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 json validTaskitem() {
|
||||
return {
|
||||
{"id", "ti-528"},
|
||||
{"nodeId", "n-1"},
|
||||
{"language", "cpp"},
|
||||
{"allowedTargets", json::array({"Function"})},
|
||||
{"allowedOps", json::array({"rename", "update"})},
|
||||
{"allowedSymbols", json::array({"cursor", "count", "EditorState"})},
|
||||
{"forbiddenSymbols", json::array({"unsafeGlobal"})},
|
||||
{"expectedDiagnosticsAdd", json::array({"none"})},
|
||||
{"expectedDiagnosticsRemove", json::array()}
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<ScopedSymbol> visibleSymbols() {
|
||||
return {
|
||||
{"cursor", "Variable", 3},
|
||||
{"count", "Parameter", 3},
|
||||
{"EditorState", "Type", 0},
|
||||
{"render", "Function", 1},
|
||||
{"unsafeGlobal", "Variable", 0}
|
||||
};
|
||||
}
|
||||
|
||||
static bool hasCode(const ConstrainedExecutionResult& result,
|
||||
const std::string& code) {
|
||||
for (const auto& v : result.diagnostics.violations) {
|
||||
if (v.code == code) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void test_valid_taskitem_routes_and_executes() {
|
||||
TEST(valid_taskitem_routes_and_executes);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
ConstrainedExecutionRequest request{
|
||||
validTaskitem(), "Function", "rename", {"cursor", "count"}, visibleSymbols()
|
||||
};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(result.routed, "request should route");
|
||||
CHECK(result.executed, "request should execute");
|
||||
CHECK(result.diagnostics.ok, "diagnostics should pass");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_missing_allowed_ops_is_rejected_as_under_constrained() {
|
||||
TEST(missing_allowed_ops_is_rejected_as_under_constrained);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
auto item = validTaskitem();
|
||||
item.erase("allowedOps");
|
||||
ConstrainedExecutionRequest request{item, "Function", "rename", {"cursor"}, visibleSymbols()};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(!result.routed, "under-constrained request should not route");
|
||||
CHECK(!result.executed, "under-constrained request should not execute");
|
||||
CHECK(hasCode(result, "under_constrained_contract"), "missing schema failure code");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_missing_diagnostics_delta_is_rejected() {
|
||||
TEST(missing_diagnostics_delta_is_rejected);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
auto item = validTaskitem();
|
||||
item["expectedDiagnosticsAdd"] = json::array();
|
||||
item["expectedDiagnosticsRemove"] = json::array();
|
||||
ConstrainedExecutionRequest request{item, "Function", "rename", {"cursor"}, visibleSymbols()};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(!result.routed, "missing diagnostics delta should not route");
|
||||
CHECK(hasCode(result, "under_constrained_contract"), "schema failure code expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_illegal_operation_fails_with_actionable_diagnostics() {
|
||||
TEST(illegal_operation_fails_with_actionable_diagnostics);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
ConstrainedExecutionRequest request{
|
||||
validTaskitem(), "Function", "delete", {"cursor"}, visibleSymbols()
|
||||
};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(!result.executed, "illegal op should block execution");
|
||||
CHECK(hasCode(result, "illegal_operation"), "illegal operation code missing");
|
||||
CHECK(result.diagnostics.recommendedAction == "escalate", "illegal op should escalate");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_out_of_scope_symbol_fails_with_retry_action() {
|
||||
TEST(out_of_scope_symbol_fails_with_retry_action);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
auto item = validTaskitem();
|
||||
item["allowedSymbols"].push_back("ghost");
|
||||
ConstrainedExecutionRequest request{
|
||||
item, "Function", "rename", {"ghost"}, visibleSymbols()
|
||||
};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(!result.executed, "out-of-scope symbol should block execution");
|
||||
CHECK(hasCode(result, "out_of_scope_symbol"), "out-of-scope code missing");
|
||||
CHECK(result.diagnostics.recommendedAction == "retry", "retryable-only failure should retry");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_forbidden_symbol_fails_with_escalation() {
|
||||
TEST(forbidden_symbol_fails_with_escalation);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
ConstrainedExecutionRequest request{
|
||||
validTaskitem(), "Function", "rename", {"unsafeGlobal"}, visibleSymbols()
|
||||
};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(!result.executed, "forbidden symbol should block execution");
|
||||
CHECK(hasCode(result, "forbidden_symbol"), "forbidden symbol code missing");
|
||||
CHECK(result.diagnostics.recommendedAction == "escalate", "forbidden symbol should escalate");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_unsupported_context_fails_fast() {
|
||||
TEST(unsupported_context_fails_fast);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
ConstrainedExecutionRequest request{
|
||||
validTaskitem(), "MacroBlock", "rename", {"cursor"}, visibleSymbols()
|
||||
};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(!result.executed, "unsupported context should block execution");
|
||||
CHECK(hasCode(result, "unsupported_context"), "unsupported context code missing");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_duplicate_allowed_ops_is_rejected_by_schema_gate() {
|
||||
TEST(duplicate_allowed_ops_is_rejected_by_schema_gate);
|
||||
LegalOperationGraph graph;
|
||||
SymbolScopeExtractor extractor;
|
||||
|
||||
auto item = validTaskitem();
|
||||
item["allowedOps"] = json::array({"rename", "rename"});
|
||||
ConstrainedExecutionRequest request{item, "Function", "rename", {"cursor"}, visibleSymbols()};
|
||||
auto result = ConstrainedTaskitemIntegration::run(request, graph, extractor);
|
||||
CHECK(!result.executed, "duplicate allowedOps should fail schema gate");
|
||||
CHECK(hasCode(result, "under_constrained_contract"), "schema gate should annotate failure");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 528: Phase 27a Integration\n";
|
||||
|
||||
test_valid_taskitem_routes_and_executes(); // 1
|
||||
test_missing_allowed_ops_is_rejected_as_under_constrained(); // 2
|
||||
test_missing_diagnostics_delta_is_rejected(); // 3
|
||||
test_illegal_operation_fails_with_actionable_diagnostics(); // 4
|
||||
test_out_of_scope_symbol_fails_with_retry_action(); // 5
|
||||
test_forbidden_symbol_fails_with_escalation(); // 6
|
||||
test_unsupported_context_fails_fast(); // 7
|
||||
test_duplicate_allowed_ops_is_rejected_by_schema_gate(); // 8
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
41
progress.md
41
progress.md
@@ -8697,3 +8697,44 @@ violations with machine-readable retry/escalation payloads.
|
||||
- `editor/src/ConstraintViolationDiagnostics.h` within header-size limit (`169` <= `600`)
|
||||
- `editor/tests/step527_test.cpp` within test-file size guidance (`223` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
### Step 528: Phase 27a Integration
|
||||
**Status:** PASS (8/8 tests)
|
||||
|
||||
Integrates the Phase 27a constrained pipeline so taskitems must pass typed
|
||||
contract validation, symbol-scope extraction, legal operation checks, and
|
||||
constraint diagnostics before execution is allowed.
|
||||
|
||||
**Files added:**
|
||||
- `editor/src/ConstrainedTaskitemIntegration.h` - phase integration gate:
|
||||
- schema-first taskitem validation via Step 524 contract module
|
||||
- forbidden-aware scope snapshot extraction via Step 526
|
||||
- constraint diagnostics evaluation via Step 527
|
||||
- deterministic route/execute decision (`routed` + `executed`)
|
||||
- actionable schema-failure packet generation for under-constrained taskitems
|
||||
- `editor/tests/step528_test.cpp` - 8 tests covering:
|
||||
- full constrained path success for valid taskitem
|
||||
- schema rejection for missing required constraint fields
|
||||
- schema rejection for missing diagnostics delta and duplicate allowed ops
|
||||
- illegal operation rejection with escalation diagnostics
|
||||
- out-of-scope symbol rejection with retry diagnostics
|
||||
- forbidden symbol and unsupported-context rejection paths
|
||||
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` - `step528_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `cmake -S editor -B editor/build-native` - PASS
|
||||
- `cmake --build editor/build-native --target step528_test step527_test` - PASS
|
||||
- `./editor/build-native/step528_test` - PASS (8/8)
|
||||
- `./editor/build-native/step527_test` - PASS (12/12) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/ConstrainedTaskitemIntegration.h` within header-size limit (`86` <= `600`)
|
||||
- `editor/tests/step528_test.cpp` within test-file size guidance (`179` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
**Phase 27a totals (524-528):**
|
||||
- **Steps completed:** 5
|
||||
- **New tests in phase plan:** 56/56 passing
|
||||
- **Constraint model + diagnostics + integration:** PASS
|
||||
|
||||
Reference in New Issue
Block a user