Step 327: WorkerRegistry — Worker Abstractions (12/12 tests)
5 worker types: deterministic (intent-based), template (getter/setter), SLM/LLM (context bundle prep for external invocation), human (review marking). Registry with type lookup and canHandle filtering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1961,4 +1961,9 @@ add_executable(step326_test tests/step326_test.cpp)
|
||||
target_include_directories(step326_test PRIVATE src)
|
||||
target_link_libraries(step326_test PRIVATE nlohmann_json::nlohmann_json)
|
||||
|
||||
# Step 327: WorkerRegistry — Worker Abstractions
|
||||
add_executable(step327_test tests/step327_test.cpp)
|
||||
target_include_directories(step327_test PRIVATE src)
|
||||
target_link_libraries(step327_test PRIVATE nlohmann_json::nlohmann_json)
|
||||
|
||||
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
||||
|
||||
265
editor/src/WorkerRegistry.h
Normal file
265
editor/src/WorkerRegistry.h
Normal file
@@ -0,0 +1,265 @@
|
||||
#pragma once
|
||||
// Step 327: WorkerRegistry — Worker Abstractions
|
||||
//
|
||||
// Defines the worker interface and provides concrete implementations:
|
||||
// DeterministicWorker, TemplateWorker, SLMAgentWorker, LLMAgentWorker,
|
||||
// HumanWorker. LLM/SLM workers prepare context bundles — actual model
|
||||
// invocation happens externally via MCP.
|
||||
|
||||
#include "WorkItem.h"
|
||||
#include "RoutingEngine.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
// --- WorkerContext: context bundle for worker execution ---
|
||||
|
||||
struct WorkerContext {
|
||||
json nodeAst; // the target node's AST
|
||||
std::string bufferContent; // current buffer text (file+ context)
|
||||
json projectSummary; // compact AST summaries (project+ context)
|
||||
std::vector<json> annotations; // annotations on the target node
|
||||
std::vector<json> diagnostics; // relevant diagnostics
|
||||
std::string skeletonIntent; // @Intent annotation value
|
||||
std::string feedbackFromRejection; // if re-routed rejected item
|
||||
|
||||
json toJson() const {
|
||||
json j;
|
||||
j["nodeAst"] = nodeAst;
|
||||
j["bufferContent"] = bufferContent;
|
||||
j["projectSummary"] = projectSummary;
|
||||
j["annotations"] = annotations;
|
||||
j["diagnostics"] = diagnostics;
|
||||
j["skeletonIntent"] = skeletonIntent;
|
||||
j["feedbackFromRejection"] = feedbackFromRejection;
|
||||
return j;
|
||||
}
|
||||
};
|
||||
|
||||
// --- WorkerInterface (abstract base) ---
|
||||
|
||||
class WorkerInterface {
|
||||
public:
|
||||
virtual ~WorkerInterface() = default;
|
||||
virtual std::string type() const = 0;
|
||||
virtual bool canHandle(const WorkItem& item) const = 0;
|
||||
virtual WorkItemResult execute(const WorkItem& item,
|
||||
const WorkerContext& context) = 0;
|
||||
};
|
||||
|
||||
// --- DeterministicWorker ---
|
||||
|
||||
class DeterministicWorker : public WorkerInterface {
|
||||
public:
|
||||
std::string type() const override { return "deterministic"; }
|
||||
|
||||
bool canHandle(const WorkItem& item) const override {
|
||||
// Handles simple skeletons with local context
|
||||
return item.contextWidth == "local" || item.contextWidth.empty();
|
||||
}
|
||||
|
||||
WorkItemResult execute(const WorkItem& item,
|
||||
const WorkerContext& context) override {
|
||||
WorkItemResult result;
|
||||
|
||||
// Use skeleton intent if available
|
||||
if (!context.skeletonIntent.empty()) {
|
||||
result.generatedCode = "// Generated from intent: " +
|
||||
context.skeletonIntent;
|
||||
result.confidence = 0.9f;
|
||||
result.reasoning = "Pattern-matched from @Intent annotation";
|
||||
} else {
|
||||
// Heuristic: generate a stub based on node name
|
||||
result.generatedCode = "// TODO: implement " + item.nodeName;
|
||||
result.confidence = 0.5f;
|
||||
result.reasoning = "Heuristic stub — no @Intent annotation";
|
||||
}
|
||||
|
||||
result.tokensGenerated = static_cast<int>(result.generatedCode.size() / 4);
|
||||
result.tokensBudget = estimateContextBudget(item.contextWidth);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// --- TemplateWorker ---
|
||||
|
||||
class TemplateWorker : public WorkerInterface {
|
||||
public:
|
||||
std::string type() const override { return "template"; }
|
||||
|
||||
bool canHandle(const WorkItem& item) const override {
|
||||
return isGetterSetterPattern(item.nodeName);
|
||||
}
|
||||
|
||||
WorkItemResult execute(const WorkItem& item,
|
||||
const WorkerContext& context) override {
|
||||
WorkItemResult result;
|
||||
std::string lower = item.nodeName;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
|
||||
if (lower.substr(0, 3) == "get") {
|
||||
std::string field = item.nodeName.substr(3);
|
||||
// Lowercase first char of field
|
||||
if (!field.empty()) field[0] = std::tolower(field[0]);
|
||||
result.generatedCode = "return self." + field;
|
||||
result.confidence = 0.95f;
|
||||
result.reasoning = "Getter template for field '" + field + "'";
|
||||
} else if (lower.substr(0, 3) == "set") {
|
||||
std::string field = item.nodeName.substr(3);
|
||||
if (!field.empty()) field[0] = std::tolower(field[0]);
|
||||
result.generatedCode = "self." + field + " = value";
|
||||
result.confidence = 0.95f;
|
||||
result.reasoning = "Setter template for field '" + field + "'";
|
||||
} else if (lower.substr(0, 2) == "is") {
|
||||
std::string field = item.nodeName.substr(2);
|
||||
if (!field.empty()) field[0] = std::tolower(field[0]);
|
||||
result.generatedCode = "return self." + field;
|
||||
result.confidence = 0.95f;
|
||||
result.reasoning = "Boolean accessor template for field '" + field + "'";
|
||||
} else {
|
||||
result.generatedCode = "// template: " + item.nodeName;
|
||||
result.confidence = 0.5f;
|
||||
result.reasoning = "No matching template pattern";
|
||||
}
|
||||
|
||||
result.tokensGenerated = static_cast<int>(result.generatedCode.size() / 4);
|
||||
result.tokensBudget = estimateContextBudget(item.contextWidth);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// --- SLMAgentWorker ---
|
||||
|
||||
class SLMAgentWorker : public WorkerInterface {
|
||||
public:
|
||||
std::string type() const override { return "slm"; }
|
||||
|
||||
bool canHandle(const WorkItem& item) const override {
|
||||
return item.contextWidth == "local" || item.contextWidth == "file";
|
||||
}
|
||||
|
||||
WorkItemResult execute(const WorkItem& item,
|
||||
const WorkerContext& context) override {
|
||||
WorkItemResult result;
|
||||
// Prepare context bundle — actual model invocation is external
|
||||
result.generatedCode = ""; // deferred to external SLM
|
||||
result.confidence = 0.0f;
|
||||
result.tokensBudget = estimateContextBudget(item.contextWidth);
|
||||
result.tokensGenerated = 0;
|
||||
|
||||
json contextBundle = context.toJson();
|
||||
contextBundle["workerType"] = "slm";
|
||||
contextBundle["itemId"] = item.id;
|
||||
contextBundle["nodeName"] = item.nodeName;
|
||||
result.astJson = contextBundle;
|
||||
result.reasoning = "Context prepared for SLM invocation via MCP";
|
||||
|
||||
if (!context.feedbackFromRejection.empty()) {
|
||||
result.reasoning += " (re-routed with feedback: " +
|
||||
context.feedbackFromRejection + ")";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// --- LLMAgentWorker ---
|
||||
|
||||
class LLMAgentWorker : public WorkerInterface {
|
||||
public:
|
||||
std::string type() const override { return "llm"; }
|
||||
|
||||
bool canHandle(const WorkItem& /*item*/) const override {
|
||||
return true; // LLM can handle anything
|
||||
}
|
||||
|
||||
WorkItemResult execute(const WorkItem& item,
|
||||
const WorkerContext& context) override {
|
||||
WorkItemResult result;
|
||||
result.generatedCode = ""; // deferred to external LLM
|
||||
result.confidence = 0.0f;
|
||||
result.tokensBudget = estimateContextBudget(item.contextWidth);
|
||||
result.tokensGenerated = 0;
|
||||
|
||||
json contextBundle = context.toJson();
|
||||
contextBundle["workerType"] = "llm";
|
||||
contextBundle["itemId"] = item.id;
|
||||
contextBundle["nodeName"] = item.nodeName;
|
||||
result.astJson = contextBundle;
|
||||
result.reasoning = "Context prepared for LLM invocation via MCP";
|
||||
|
||||
if (!context.feedbackFromRejection.empty()) {
|
||||
result.reasoning += " (re-routed with feedback: " +
|
||||
context.feedbackFromRejection + ")";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// --- HumanWorker ---
|
||||
|
||||
class HumanWorker : public WorkerInterface {
|
||||
public:
|
||||
std::string type() const override { return "human"; }
|
||||
|
||||
bool canHandle(const WorkItem& /*item*/) const override {
|
||||
return true; // human can handle anything
|
||||
}
|
||||
|
||||
WorkItemResult execute(const WorkItem& item,
|
||||
const WorkerContext& context) override {
|
||||
WorkItemResult result;
|
||||
result.generatedCode = ""; // deferred to human
|
||||
result.confidence = 0.0f;
|
||||
result.tokensBudget = 0;
|
||||
result.tokensGenerated = 0;
|
||||
|
||||
json contextBundle = context.toJson();
|
||||
contextBundle["workerType"] = "human";
|
||||
contextBundle["itemId"] = item.id;
|
||||
contextBundle["nodeName"] = item.nodeName;
|
||||
contextBundle["status"] = "needs-human";
|
||||
result.astJson = contextBundle;
|
||||
result.reasoning = "Routed to human review — requires manual implementation";
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// --- WorkerRegistry ---
|
||||
|
||||
class WorkerRegistry {
|
||||
public:
|
||||
void registerWorker(std::unique_ptr<WorkerInterface> worker) {
|
||||
std::string t = worker->type();
|
||||
workers_[t] = std::move(worker);
|
||||
}
|
||||
|
||||
WorkerInterface* getWorker(const std::string& workerType) const {
|
||||
auto it = workers_.find(workerType);
|
||||
if (it != workers_.end()) return it->second.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> registeredTypes() const {
|
||||
std::vector<std::string> types;
|
||||
for (const auto& [k, _] : workers_) {
|
||||
types.push_back(k);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
static WorkerRegistry getDefaultRegistry() {
|
||||
WorkerRegistry reg;
|
||||
reg.registerWorker(std::make_unique<DeterministicWorker>());
|
||||
reg.registerWorker(std::make_unique<TemplateWorker>());
|
||||
reg.registerWorker(std::make_unique<SLMAgentWorker>());
|
||||
reg.registerWorker(std::make_unique<LLMAgentWorker>());
|
||||
reg.registerWorker(std::make_unique<HumanWorker>());
|
||||
return reg;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, std::unique_ptr<WorkerInterface>> workers_;
|
||||
};
|
||||
251
editor/tests/step327_test.cpp
Normal file
251
editor/tests/step327_test.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
// Step 327: WorkerRegistry — Worker Abstractions (12 tests)
|
||||
// Tests worker interface, concrete workers, registry lookup,
|
||||
// context assembly, canHandle, rejection feedback.
|
||||
|
||||
#include "WorkerRegistry.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
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 WorkItem makeItem(const std::string& name,
|
||||
const std::string& contextWidth = "local") {
|
||||
WorkItem wi;
|
||||
wi.id = "wi-" + name;
|
||||
wi.nodeId = "n-" + name;
|
||||
wi.nodeName = name;
|
||||
wi.nodeType = "Function";
|
||||
wi.bufferId = "buf";
|
||||
wi.contextWidth = contextWidth;
|
||||
wi.priority = "medium";
|
||||
wi.createdAt = workItemTimestamp();
|
||||
return wi;
|
||||
}
|
||||
|
||||
static WorkerContext makeContext(const std::string& intent = "",
|
||||
const std::string& feedback = "") {
|
||||
WorkerContext ctx;
|
||||
ctx.nodeAst = json{{"type", "Function"}, {"name", "test"}};
|
||||
ctx.bufferContent = "# test buffer";
|
||||
ctx.skeletonIntent = intent;
|
||||
ctx.feedbackFromRejection = feedback;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// 1. Deterministic worker produces code for simple skeleton
|
||||
void test_deterministic_worker() {
|
||||
TEST(deterministic_worker);
|
||||
DeterministicWorker w;
|
||||
CHECK(w.type() == "deterministic", "type");
|
||||
|
||||
auto wi = makeItem("compute");
|
||||
auto ctx = makeContext("return x + y");
|
||||
auto result = w.execute(wi, ctx);
|
||||
|
||||
CHECK(!result.generatedCode.empty(), "produces code");
|
||||
CHECK(result.confidence > 0.8f, "high confidence with intent");
|
||||
CHECK(result.reasoning.find("Intent") != std::string::npos, "reasoning mentions intent");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 2. Template worker fills getter pattern
|
||||
void test_template_getter() {
|
||||
TEST(template_getter);
|
||||
TemplateWorker w;
|
||||
CHECK(w.type() == "template", "type");
|
||||
|
||||
auto wi = makeItem("getName");
|
||||
auto ctx = makeContext();
|
||||
auto result = w.execute(wi, ctx);
|
||||
|
||||
CHECK(result.generatedCode.find("self.name") != std::string::npos,
|
||||
"getter returns field: " + result.generatedCode);
|
||||
CHECK(result.confidence > 0.9f, "high confidence");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 3. Template worker fills setter pattern
|
||||
void test_template_setter() {
|
||||
TEST(template_setter);
|
||||
TemplateWorker w;
|
||||
auto wi = makeItem("setValue");
|
||||
auto ctx = makeContext();
|
||||
auto result = w.execute(wi, ctx);
|
||||
|
||||
CHECK(result.generatedCode.find("self.value") != std::string::npos,
|
||||
"setter sets field: " + result.generatedCode);
|
||||
CHECK(result.confidence > 0.9f, "high confidence");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 4. Agent worker (LLM) prepares context bundle
|
||||
void test_llm_agent_context() {
|
||||
TEST(llm_agent_context);
|
||||
LLMAgentWorker w;
|
||||
CHECK(w.type() == "llm", "type");
|
||||
|
||||
auto wi = makeItem("complexAlgo", "project");
|
||||
auto ctx = makeContext();
|
||||
ctx.projectSummary = json{{"buffers", 3}};
|
||||
auto result = w.execute(wi, ctx);
|
||||
|
||||
CHECK(result.confidence == 0.0f, "deferred confidence");
|
||||
CHECK(result.generatedCode.empty(), "no code generated");
|
||||
CHECK(result.astJson.contains("workerType"), "has workerType in bundle");
|
||||
CHECK(result.astJson["workerType"] == "llm", "llm in bundle");
|
||||
CHECK(result.tokensBudget == 8000, "project budget");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 5. Human worker marks for review
|
||||
void test_human_worker() {
|
||||
TEST(human_worker);
|
||||
HumanWorker w;
|
||||
CHECK(w.type() == "human", "type");
|
||||
|
||||
auto wi = makeItem("ambiguousLogic");
|
||||
auto ctx = makeContext();
|
||||
auto result = w.execute(wi, ctx);
|
||||
|
||||
CHECK(result.confidence == 0.0f, "deferred confidence");
|
||||
CHECK(result.astJson.contains("status"), "has status");
|
||||
CHECK(result.astJson["status"] == "needs-human", "needs-human status");
|
||||
CHECK(result.reasoning.find("human") != std::string::npos, "reasoning");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 6. Registry lookup
|
||||
void test_registry_lookup() {
|
||||
TEST(registry_lookup);
|
||||
auto reg = WorkerRegistry::getDefaultRegistry();
|
||||
|
||||
CHECK(reg.getWorker("deterministic") != nullptr, "has deterministic");
|
||||
CHECK(reg.getWorker("template") != nullptr, "has template");
|
||||
CHECK(reg.getWorker("slm") != nullptr, "has slm");
|
||||
CHECK(reg.getWorker("llm") != nullptr, "has llm");
|
||||
CHECK(reg.getWorker("human") != nullptr, "has human");
|
||||
CHECK(reg.getWorker("nonexistent") == nullptr, "null for unknown");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 7. canHandle filtering
|
||||
void test_can_handle() {
|
||||
TEST(can_handle);
|
||||
DeterministicWorker det;
|
||||
TemplateWorker tmpl;
|
||||
SLMAgentWorker slm;
|
||||
LLMAgentWorker llm;
|
||||
|
||||
auto localItem = makeItem("compute", "local");
|
||||
auto projectItem = makeItem("compute", "project");
|
||||
auto getterItem = makeItem("getName", "local");
|
||||
|
||||
CHECK(det.canHandle(localItem), "deterministic handles local");
|
||||
CHECK(!det.canHandle(projectItem), "deterministic rejects project");
|
||||
CHECK(tmpl.canHandle(getterItem), "template handles getter");
|
||||
CHECK(!tmpl.canHandle(localItem), "template rejects non-getter");
|
||||
CHECK(slm.canHandle(localItem), "slm handles local");
|
||||
CHECK(!slm.canHandle(projectItem), "slm rejects project");
|
||||
CHECK(llm.canHandle(projectItem), "llm handles anything");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 8. Worker type strings
|
||||
void test_worker_type_strings() {
|
||||
TEST(worker_type_strings);
|
||||
auto reg = WorkerRegistry::getDefaultRegistry();
|
||||
auto types = reg.registeredTypes();
|
||||
std::set<std::string> typeSet(types.begin(), types.end());
|
||||
|
||||
CHECK(typeSet.count("deterministic"), "has deterministic");
|
||||
CHECK(typeSet.count("template"), "has template");
|
||||
CHECK(typeSet.count("slm"), "has slm");
|
||||
CHECK(typeSet.count("llm"), "has llm");
|
||||
CHECK(typeSet.count("human"), "has human");
|
||||
CHECK(typeSet.size() == 5, "5 types total");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 9. Context assembly — SLM includes context bundle
|
||||
void test_slm_context_assembly() {
|
||||
TEST(slm_context_assembly);
|
||||
SLMAgentWorker w;
|
||||
auto wi = makeItem("parseInput", "file");
|
||||
auto ctx = makeContext();
|
||||
ctx.bufferContent = "def parseInput(data): pass";
|
||||
|
||||
auto result = w.execute(wi, ctx);
|
||||
CHECK(result.astJson.contains("bufferContent"), "includes buffer content");
|
||||
CHECK(result.astJson["workerType"] == "slm", "slm in bundle");
|
||||
CHECK(result.tokensBudget == 2000, "file budget");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 10. Rejection feedback included in context
|
||||
void test_rejection_feedback() {
|
||||
TEST(rejection_feedback);
|
||||
LLMAgentWorker w;
|
||||
auto wi = makeItem("retryFunc", "project");
|
||||
auto ctx = makeContext("", "needs better error handling");
|
||||
|
||||
auto result = w.execute(wi, ctx);
|
||||
CHECK(result.reasoning.find("feedback") != std::string::npos, "feedback in reasoning");
|
||||
CHECK(result.reasoning.find("error handling") != std::string::npos,
|
||||
"specific feedback preserved");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 11. Deterministic worker without intent → low confidence
|
||||
void test_deterministic_no_intent() {
|
||||
TEST(deterministic_no_intent);
|
||||
DeterministicWorker w;
|
||||
auto wi = makeItem("mystery");
|
||||
auto ctx = makeContext(); // no intent
|
||||
|
||||
auto result = w.execute(wi, ctx);
|
||||
CHECK(result.confidence < 0.6f, "low confidence without intent");
|
||||
CHECK(result.generatedCode.find("TODO") != std::string::npos, "generates stub");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 12. Boolean accessor (isX) template
|
||||
void test_boolean_accessor() {
|
||||
TEST(boolean_accessor);
|
||||
TemplateWorker w;
|
||||
auto wi = makeItem("isActive");
|
||||
auto ctx = makeContext();
|
||||
auto result = w.execute(wi, ctx);
|
||||
|
||||
CHECK(result.generatedCode.find("self.active") != std::string::npos,
|
||||
"boolean accessor: " + result.generatedCode);
|
||||
CHECK(result.confidence > 0.9f, "high confidence");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "=== Step 327: WorkerRegistry Worker Abstractions ===\n";
|
||||
try {
|
||||
test_deterministic_worker();
|
||||
test_template_getter();
|
||||
test_template_setter();
|
||||
test_llm_agent_context();
|
||||
test_human_worker();
|
||||
test_registry_lookup();
|
||||
test_can_handle();
|
||||
test_worker_type_strings();
|
||||
test_slm_context_assembly();
|
||||
test_rejection_feedback();
|
||||
test_deterministic_no_intent();
|
||||
test_boolean_accessor();
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "EXCEPTION: " << e.what() << "\n";
|
||||
++failed;
|
||||
}
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
|
||||
return failed > 0 ? 1 : 0;
|
||||
}
|
||||
20
progress.md
20
progress.md
@@ -1354,6 +1354,26 @@ context width escalation → getter/setter patterns → defaults.
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` — step326_test target
|
||||
|
||||
### Step 327: WorkerRegistry — Worker Abstractions
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Worker interface with 5 concrete implementations: DeterministicWorker
|
||||
(intent-based code gen), TemplateWorker (getter/setter/accessor patterns),
|
||||
SLMAgentWorker/LLMAgentWorker (context bundle preparation for external
|
||||
invocation), HumanWorker (marks for human review). Registry with lookup.
|
||||
|
||||
**Files created:**
|
||||
- `editor/src/WorkerRegistry.h` — WorkerContext struct, WorkerInterface
|
||||
abstract base, DeterministicWorker, TemplateWorker, SLMAgentWorker,
|
||||
LLMAgentWorker, HumanWorker, WorkerRegistry with getDefaultRegistry
|
||||
- `editor/tests/step327_test.cpp` — 12 tests: deterministic with/without
|
||||
intent, template getter/setter/boolean accessor, LLM context bundle,
|
||||
human review marking, registry lookup, canHandle filtering, worker type
|
||||
strings, SLM context assembly, rejection feedback in context
|
||||
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` — step327_test target
|
||||
|
||||
---
|
||||
|
||||
# Roadmap Planning — Sprints 12-25+
|
||||
|
||||
Reference in New Issue
Block a user