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:
@@ -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
220
editor/src/WorkItem.h
Normal 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;
|
||||
}
|
||||
285
editor/tests/step320_test.cpp
Normal file
285
editor/tests/step320_test.cpp
Normal 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;
|
||||
}
|
||||
29
progress.md
29
progress.md
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user