Step 528: integrate constrained taskitem execution gate

This commit is contained in:
Bill
2026-02-17 09:20:10 -07:00
parent b1f95cd699
commit b8667457b2
4 changed files with 315 additions and 0 deletions

View File

@@ -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)

View 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;
}
};

View 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;
}

View File

@@ -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