Step 320: WorkItem — Core Execution Model (12/12 tests)

Sprint 12 Phase 12a start. WorkItem extends SkeletonTask with execution
lifecycle tracking: 7-state machine, worker assignment, timestamps, results,
and full JSON serialization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bill
2026-02-15 16:17:35 -07:00
parent 0d51a6fe4c
commit 25ab38d185
4 changed files with 539 additions and 0 deletions

View File

@@ -1916,4 +1916,9 @@ target_link_libraries(step318_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
# Step 320: WorkItem — Core Execution Model
add_executable(step320_test tests/step320_test.cpp)
target_include_directories(step320_test PRIVATE src)
target_link_libraries(step320_test PRIVATE nlohmann_json::nlohmann_json)
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)

220
editor/src/WorkItem.h Normal file
View File

@@ -0,0 +1,220 @@
#pragma once
// Step 320: WorkItem — Core Execution Model
//
// Extends SkeletonTask with execution lifecycle tracking: status transitions,
// worker assignment, timestamps, and results. This is the unit of work that
// flows through the routing engine.
#include "SkeletonAST.h"
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include <chrono>
#include <random>
#include <sstream>
#include <iomanip>
using json = nlohmann::json;
// --- WorkItemResult: output produced by a worker ---
struct WorkItemResult {
std::string generatedCode;
json astJson;
std::vector<json> diagnostics;
float confidence = 0.0f;
int tokensBudget = 0;
int tokensGenerated = 0;
std::string reasoning;
json toJson() const {
json j;
j["generatedCode"] = generatedCode;
j["astJson"] = astJson;
j["diagnostics"] = diagnostics;
j["confidence"] = confidence;
j["tokensBudget"] = tokensBudget;
j["tokensGenerated"] = tokensGenerated;
j["reasoning"] = reasoning;
return j;
}
static WorkItemResult fromJson(const json& j) {
WorkItemResult r;
if (j.contains("generatedCode")) r.generatedCode = j["generatedCode"].get<std::string>();
if (j.contains("astJson")) r.astJson = j["astJson"];
if (j.contains("diagnostics")) r.diagnostics = j["diagnostics"].get<std::vector<json>>();
if (j.contains("confidence")) r.confidence = j["confidence"].get<float>();
if (j.contains("tokensBudget")) r.tokensBudget = j["tokensBudget"].get<int>();
if (j.contains("tokensGenerated")) r.tokensGenerated = j["tokensGenerated"].get<int>();
if (j.contains("reasoning")) r.reasoning = j["reasoning"].get<std::string>();
return r;
}
};
// --- WorkItem status values ---
inline const std::string WI_PENDING = "pending";
inline const std::string WI_READY = "ready";
inline const std::string WI_ASSIGNED = "assigned";
inline const std::string WI_IN_PROGRESS = "in-progress";
inline const std::string WI_REVIEW = "review";
inline const std::string WI_COMPLETE = "complete";
inline const std::string WI_REJECTED = "rejected";
// --- WorkItem struct ---
struct WorkItem {
std::string id;
std::string nodeId;
std::string nodeName;
std::string nodeType;
std::string bufferId;
// Routing
std::string contextWidth;
std::string workerType;
bool reviewRequired = false;
std::string priority;
std::vector<std::string> dependencies;
// Lifecycle
std::string status = WI_PENDING;
// Assignment
std::string assignee;
std::string createdAt;
std::string assignedAt;
std::string completedAt;
// Result
WorkItemResult result;
};
// --- Helper: ISO timestamp ---
inline std::string workItemTimestamp() {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
gmtime_r(&time, &tm);
std::ostringstream oss;
oss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
return oss.str();
}
// --- Helper: generate unique ID ---
inline std::string generateWorkItemId() {
static std::mt19937 rng(std::random_device{}());
static std::uniform_int_distribution<uint64_t> dist;
std::ostringstream oss;
oss << "wi-" << std::hex << dist(rng);
return oss.str();
}
// --- createWorkItem from SkeletonTask ---
inline WorkItem createWorkItem(const SkeletonTask& task, const std::string& bufferId) {
WorkItem wi;
wi.id = generateWorkItemId();
wi.nodeId = task.nodeId;
wi.nodeName = task.nodeName;
wi.nodeType = task.nodeType;
wi.bufferId = bufferId;
wi.contextWidth = task.contextWidth;
wi.workerType = task.automatability;
wi.reviewRequired = task.reviewRequired;
wi.priority = task.priority;
wi.dependencies = task.dependencies;
wi.status = WI_PENDING;
wi.createdAt = workItemTimestamp();
return wi;
}
// --- State machine helpers ---
inline bool isTerminal(const std::string& status) {
return status == WI_COMPLETE || status == WI_REJECTED;
}
inline bool canTransition(const std::string& from, const std::string& to) {
// pending → ready
if (from == WI_PENDING && to == WI_READY) return true;
// ready → assigned
if (from == WI_READY && to == WI_ASSIGNED) return true;
// assigned → in-progress
if (from == WI_ASSIGNED && to == WI_IN_PROGRESS) return true;
// in-progress → review | complete
if (from == WI_IN_PROGRESS && (to == WI_REVIEW || to == WI_COMPLETE)) return true;
// review → complete | rejected
if (from == WI_REVIEW && (to == WI_COMPLETE || to == WI_REJECTED)) return true;
// rejected → ready (retry)
if (from == WI_REJECTED && to == WI_READY) return true;
return false;
}
inline bool transitionWorkItem(WorkItem& item, const std::string& newStatus) {
if (!canTransition(item.status, newStatus)) return false;
item.status = newStatus;
if (newStatus == WI_ASSIGNED) {
item.assignedAt = workItemTimestamp();
} else if (newStatus == WI_COMPLETE || newStatus == WI_REJECTED) {
item.completedAt = workItemTimestamp();
}
return true;
}
// --- Priority ordering ---
inline int priorityToInt(const std::string& p) {
if (p == "critical") return 0;
if (p == "high") return 1;
if (p == "medium") return 2;
if (p == "low") return 3;
return 4; // unknown
}
// --- JSON serialization ---
inline json workItemToJson(const WorkItem& wi) {
json j;
j["id"] = wi.id;
j["nodeId"] = wi.nodeId;
j["nodeName"] = wi.nodeName;
j["nodeType"] = wi.nodeType;
j["bufferId"] = wi.bufferId;
j["contextWidth"] = wi.contextWidth;
j["workerType"] = wi.workerType;
j["reviewRequired"] = wi.reviewRequired;
j["priority"] = wi.priority;
j["dependencies"] = wi.dependencies;
j["status"] = wi.status;
j["assignee"] = wi.assignee;
j["createdAt"] = wi.createdAt;
j["assignedAt"] = wi.assignedAt;
j["completedAt"] = wi.completedAt;
j["result"] = wi.result.toJson();
return j;
}
inline WorkItem workItemFromJson(const json& j) {
WorkItem wi;
if (j.contains("id")) wi.id = j["id"].get<std::string>();
if (j.contains("nodeId")) wi.nodeId = j["nodeId"].get<std::string>();
if (j.contains("nodeName")) wi.nodeName = j["nodeName"].get<std::string>();
if (j.contains("nodeType")) wi.nodeType = j["nodeType"].get<std::string>();
if (j.contains("bufferId")) wi.bufferId = j["bufferId"].get<std::string>();
if (j.contains("contextWidth")) wi.contextWidth = j["contextWidth"].get<std::string>();
if (j.contains("workerType")) wi.workerType = j["workerType"].get<std::string>();
if (j.contains("reviewRequired")) wi.reviewRequired = j["reviewRequired"].get<bool>();
if (j.contains("priority")) wi.priority = j["priority"].get<std::string>();
if (j.contains("dependencies")) wi.dependencies = j["dependencies"].get<std::vector<std::string>>();
if (j.contains("status")) wi.status = j["status"].get<std::string>();
if (j.contains("assignee")) wi.assignee = j["assignee"].get<std::string>();
if (j.contains("createdAt")) wi.createdAt = j["createdAt"].get<std::string>();
if (j.contains("assignedAt")) wi.assignedAt = j["assignedAt"].get<std::string>();
if (j.contains("completedAt")) wi.completedAt = j["completedAt"].get<std::string>();
if (j.contains("result")) wi.result = WorkItemResult::fromJson(j["result"]);
return wi;
}

View File

@@ -0,0 +1,285 @@
// Step 320: WorkItem — Core Execution Model (12 tests)
// Tests lifecycle state machine, JSON roundtrip, timestamps, priority ordering.
#include "WorkItem.h"
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <set>
using json = nlohmann::json;
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 {}
// 1. WorkItem construction from SkeletonTask — fields copied correctly
void test_create_from_skeleton() {
TEST(create_from_skeleton);
SkeletonTask task;
task.nodeId = "fn_42";
task.nodeName = "processData";
task.nodeType = "Function";
task.contextWidth = "module";
task.automatability = "llm";
task.priority = "high";
task.reviewRequired = true;
task.dependencies = {"fn_10", "fn_20"};
WorkItem wi = createWorkItem(task, "buf-1");
CHECK(wi.nodeId == "fn_42", "nodeId");
CHECK(wi.nodeName == "processData", "nodeName");
CHECK(wi.nodeType == "Function", "nodeType");
CHECK(wi.bufferId == "buf-1", "bufferId");
CHECK(wi.contextWidth == "module", "contextWidth");
CHECK(wi.workerType == "llm", "workerType");
CHECK(wi.priority == "high", "priority");
CHECK(wi.reviewRequired == true, "reviewRequired");
CHECK(wi.dependencies.size() == 2, "dependencies count");
CHECK(wi.status == "pending", "initial status");
PASS();
}
// 2. ID generation — unique, non-empty
void test_id_generation() {
TEST(id_generation);
SkeletonTask task;
task.nodeId = "n1";
task.nodeName = "foo";
task.nodeType = "Function";
std::set<std::string> ids;
for (int i = 0; i < 100; ++i) {
WorkItem wi = createWorkItem(task, "buf");
CHECK(!wi.id.empty(), "ID non-empty");
ids.insert(wi.id);
}
CHECK(ids.size() == 100, "All 100 IDs unique, got " + std::to_string(ids.size()));
PASS();
}
// 3. Valid state transitions
void test_valid_transitions() {
TEST(valid_transitions);
CHECK(canTransition("pending", "ready"), "pending→ready");
CHECK(canTransition("ready", "assigned"), "ready→assigned");
CHECK(canTransition("assigned", "in-progress"), "assigned→in-progress");
CHECK(canTransition("in-progress", "review"), "in-progress→review");
CHECK(canTransition("in-progress", "complete"), "in-progress→complete");
CHECK(canTransition("review", "complete"), "review→complete");
CHECK(canTransition("review", "rejected"), "review→rejected");
CHECK(canTransition("rejected", "ready"), "rejected→ready");
PASS();
}
// 4. Invalid state transitions rejected
void test_invalid_transitions() {
TEST(invalid_transitions);
CHECK(!canTransition("pending", "complete"), "pending→complete blocked");
CHECK(!canTransition("complete", "ready"), "complete→ready blocked");
CHECK(!canTransition("pending", "in-progress"), "pending→in-progress blocked");
CHECK(!canTransition("assigned", "complete"), "assigned→complete blocked");
CHECK(!canTransition("ready", "review"), "ready→review blocked");
CHECK(!canTransition("complete", "pending"), "complete→pending blocked");
PASS();
}
// 5. transitionWorkItem populates timestamps
void test_transition_timestamps() {
TEST(transition_timestamps);
SkeletonTask task;
task.nodeId = "n1";
task.nodeName = "foo";
task.nodeType = "Function";
WorkItem wi = createWorkItem(task, "buf");
CHECK(wi.assignedAt.empty(), "assignedAt initially empty");
CHECK(wi.completedAt.empty(), "completedAt initially empty");
transitionWorkItem(wi, "ready");
transitionWorkItem(wi, "assigned");
CHECK(!wi.assignedAt.empty(), "assignedAt set on assigned");
transitionWorkItem(wi, "in-progress");
transitionWorkItem(wi, "complete");
CHECK(!wi.completedAt.empty(), "completedAt set on complete");
PASS();
}
// 6. WorkItemResult toJson/fromJson roundtrip
void test_result_roundtrip() {
TEST(result_roundtrip);
WorkItemResult r;
r.generatedCode = "def foo(): return 42";
r.astJson = json::parse(R"({"type":"Function","name":"foo"})");
r.diagnostics = {json{{"level", "warning"}, {"msg", "unused var"}}};
r.confidence = 0.95f;
r.tokensBudget = 4000;
r.tokensGenerated = 1200;
r.reasoning = "Simple pure function";
json j = r.toJson();
WorkItemResult r2 = WorkItemResult::fromJson(j);
CHECK(r2.generatedCode == r.generatedCode, "generatedCode");
CHECK(r2.astJson == r.astJson, "astJson");
CHECK(r2.diagnostics.size() == 1, "diagnostics count");
CHECK(r2.confidence > 0.94f && r2.confidence < 0.96f, "confidence");
CHECK(r2.tokensBudget == 4000, "tokensBudget");
CHECK(r2.tokensGenerated == 1200, "tokensGenerated");
CHECK(r2.reasoning == "Simple pure function", "reasoning");
PASS();
}
// 7. WorkItem toJson/fromJson roundtrip — all fields preserved
void test_workitem_roundtrip() {
TEST(workitem_roundtrip);
WorkItem wi;
wi.id = "wi-abc123";
wi.nodeId = "fn_1";
wi.nodeName = "compute";
wi.nodeType = "Function";
wi.bufferId = "buf-main";
wi.contextWidth = "project";
wi.workerType = "llm";
wi.reviewRequired = true;
wi.priority = "critical";
wi.dependencies = {"fn_0"};
wi.status = "in-progress";
wi.assignee = "worker-7";
wi.createdAt = "2026-01-01T00:00:00Z";
wi.assignedAt = "2026-01-01T00:01:00Z";
wi.completedAt = "";
wi.result.generatedCode = "int x = 1;";
wi.result.confidence = 0.8f;
json j = workItemToJson(wi);
WorkItem wi2 = workItemFromJson(j);
CHECK(wi2.id == wi.id, "id");
CHECK(wi2.nodeId == wi.nodeId, "nodeId");
CHECK(wi2.nodeName == wi.nodeName, "nodeName");
CHECK(wi2.nodeType == wi.nodeType, "nodeType");
CHECK(wi2.bufferId == wi.bufferId, "bufferId");
CHECK(wi2.contextWidth == wi.contextWidth, "contextWidth");
CHECK(wi2.workerType == wi.workerType, "workerType");
CHECK(wi2.reviewRequired == wi.reviewRequired, "reviewRequired");
CHECK(wi2.priority == wi.priority, "priority");
CHECK(wi2.dependencies.size() == 1, "dependencies");
CHECK(wi2.status == wi.status, "status");
CHECK(wi2.assignee == wi.assignee, "assignee");
CHECK(wi2.createdAt == wi.createdAt, "createdAt");
CHECK(wi2.assignedAt == wi.assignedAt, "assignedAt");
CHECK(wi2.result.generatedCode == "int x = 1;", "result.generatedCode");
PASS();
}
// 8. isTerminal — complete=true, rejected=true, others=false
void test_is_terminal() {
TEST(is_terminal);
CHECK(isTerminal("complete"), "complete is terminal");
CHECK(isTerminal("rejected"), "rejected is terminal");
CHECK(!isTerminal("pending"), "pending not terminal");
CHECK(!isTerminal("ready"), "ready not terminal");
CHECK(!isTerminal("assigned"), "assigned not terminal");
CHECK(!isTerminal("in-progress"), "in-progress not terminal");
CHECK(!isTerminal("review"), "review not terminal");
PASS();
}
// 9. createWorkItem sets createdAt timestamp
void test_created_at_timestamp() {
TEST(created_at_timestamp);
SkeletonTask task;
task.nodeId = "n1";
task.nodeName = "bar";
task.nodeType = "Function";
WorkItem wi = createWorkItem(task, "buf");
CHECK(!wi.createdAt.empty(), "createdAt non-empty");
// Should look like ISO timestamp: starts with 20
CHECK(wi.createdAt.substr(0, 2) == "20", "createdAt starts with year");
CHECK(wi.createdAt.find('T') != std::string::npos, "createdAt has T separator");
PASS();
}
// 10. Dependencies preserved from SkeletonTask
void test_dependencies_preserved() {
TEST(dependencies_preserved);
SkeletonTask task;
task.nodeId = "n5";
task.nodeName = "aggregate";
task.nodeType = "Function";
task.dependencies = {"n1", "n2", "n3"};
WorkItem wi = createWorkItem(task, "buf");
CHECK(wi.dependencies.size() == 3, "3 dependencies");
CHECK(wi.dependencies[0] == "n1", "dep 0");
CHECK(wi.dependencies[1] == "n2", "dep 1");
CHECK(wi.dependencies[2] == "n3", "dep 2");
PASS();
}
// 11. priorityToInt ordering (critical < high < medium < low)
void test_priority_ordering() {
TEST(priority_ordering);
CHECK(priorityToInt("critical") < priorityToInt("high"), "critical < high");
CHECK(priorityToInt("high") < priorityToInt("medium"), "high < medium");
CHECK(priorityToInt("medium") < priorityToInt("low"), "medium < low");
CHECK(priorityToInt("critical") == 0, "critical=0");
CHECK(priorityToInt("low") == 3, "low=3");
PASS();
}
// 12. Result attachment — set result, verify in JSON roundtrip
void test_result_attachment() {
TEST(result_attachment);
SkeletonTask task;
task.nodeId = "n1";
task.nodeName = "gen";
task.nodeType = "Function";
task.contextWidth = "local";
task.automatability = "llm";
task.priority = "medium";
WorkItem wi = createWorkItem(task, "buf-2");
wi.result.generatedCode = "return 42;";
wi.result.confidence = 0.99f;
wi.result.tokensBudget = 2000;
wi.result.tokensGenerated = 50;
wi.result.reasoning = "trivial";
wi.result.diagnostics = {json{{"level", "info"}, {"msg", "ok"}}};
json j = workItemToJson(wi);
WorkItem wi2 = workItemFromJson(j);
CHECK(wi2.result.generatedCode == "return 42;", "code preserved");
CHECK(wi2.result.confidence > 0.98f, "confidence preserved");
CHECK(wi2.result.tokensBudget == 2000, "budget preserved");
CHECK(wi2.result.tokensGenerated == 50, "generated preserved");
CHECK(wi2.result.reasoning == "trivial", "reasoning preserved");
CHECK(wi2.result.diagnostics.size() == 1, "diagnostics preserved");
PASS();
}
int main() {
std::cout << "=== Step 320: WorkItem Core Execution Model ===\n";
try {
test_create_from_skeleton();
test_id_generation();
test_valid_transitions();
test_invalid_transitions();
test_transition_timestamps();
test_result_roundtrip();
test_workitem_roundtrip();
test_is_terminal();
test_created_at_timestamp();
test_dependencies_preserved();
test_priority_ordering();
test_result_attachment();
} 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

@@ -1199,6 +1199,35 @@ Full pipeline integration for Kotlin + C#:
---
# Sprint 12 — Workflow Model + C++ Depth
## Phase 12a: Core Workflow Model
### Step 320: WorkItem — Core Execution Model
**Status:** PASS (12/12 tests)
WorkItem extends SkeletonTask with execution lifecycle tracking — status
transitions, worker assignment, timestamps, and results. This is the unit of
work that flows through the routing engine.
**Files created:**
- `editor/src/WorkItem.h` — WorkItemResult struct (generatedCode, astJson,
diagnostics, confidence, tokens, reasoning + JSON roundtrip), WorkItem struct
(identity, routing, lifecycle, assignment, result), state machine helpers
(isTerminal, canTransition, transitionWorkItem), createWorkItem from
SkeletonTask, priorityToInt ordering, full JSON serialization
- `editor/tests/step320_test.cpp` — 12 tests: construction from SkeletonTask,
unique ID generation, valid/invalid state transitions, timestamp population,
WorkItemResult roundtrip, WorkItem roundtrip, isTerminal, createdAt,
dependencies preserved, priority ordering, result attachment roundtrip
**Files modified:**
- `editor/CMakeLists.txt` — step320_test target
**State machine:** pending→ready→assigned→in-progress→(review→complete|rejected OR complete); rejected→ready
---
# Roadmap Planning — Sprints 12-25+
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)