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:
Bill
2026-02-15 19:11:36 -07:00
parent 2a2aef3fef
commit 63de321b6d
4 changed files with 541 additions and 0 deletions

View File

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

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

View File

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