codeharness 0.31.7 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2895,7 +2895,7 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
2895
2895
|
}
|
|
2896
2896
|
|
|
2897
2897
|
// src/modules/infra/init-project.ts
|
|
2898
|
-
var HARNESS_VERSION = true ? "0.
|
|
2898
|
+
var HARNESS_VERSION = true ? "0.32.0" : "0.0.0-dev";
|
|
2899
2899
|
function failResult(opts, error) {
|
|
2900
2900
|
return {
|
|
2901
2901
|
status: "fail",
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
validateDockerfile,
|
|
41
41
|
warn,
|
|
42
42
|
writeState
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-IZWRIAGV.js";
|
|
44
44
|
|
|
45
45
|
// src/index.ts
|
|
46
46
|
import { Command } from "commander";
|
|
@@ -1604,7 +1604,7 @@ var workflow_schema_default = {
|
|
|
1604
1604
|
title: "Codeharness Workflow",
|
|
1605
1605
|
description: "Schema for codeharness workflow YAML files",
|
|
1606
1606
|
type: "object",
|
|
1607
|
-
required: ["tasks"],
|
|
1607
|
+
required: ["tasks", "story_flow", "epic_flow"],
|
|
1608
1608
|
properties: {
|
|
1609
1609
|
tasks: {
|
|
1610
1610
|
type: "object",
|
|
@@ -1706,12 +1706,6 @@ var workflow_schema_default = {
|
|
|
1706
1706
|
],
|
|
1707
1707
|
description: "Agent identifier for this task (null for engine-handled tasks)"
|
|
1708
1708
|
},
|
|
1709
|
-
scope: {
|
|
1710
|
-
type: "string",
|
|
1711
|
-
enum: ["per-story", "per-run", "per-epic"],
|
|
1712
|
-
default: "per-story",
|
|
1713
|
-
description: "Execution scope \u2014 per-story, per-run, or per-epic"
|
|
1714
|
-
},
|
|
1715
1709
|
session: {
|
|
1716
1710
|
type: "string",
|
|
1717
1711
|
enum: ["fresh", "continue"],
|
|
@@ -2121,7 +2115,7 @@ ${bullets}`);
|
|
|
2121
2115
|
}
|
|
2122
2116
|
|
|
2123
2117
|
// src/lib/hierarchical-flow.ts
|
|
2124
|
-
var BUILTIN_EPIC_FLOW_TASKS = /* @__PURE__ */ new Set(["merge", "validate"]);
|
|
2118
|
+
var BUILTIN_EPIC_FLOW_TASKS = /* @__PURE__ */ new Set(["merge", "validate", "story_flow"]);
|
|
2125
2119
|
var EXECUTION_DEFAULTS = {
|
|
2126
2120
|
max_parallel: 1,
|
|
2127
2121
|
isolation: "none",
|
|
@@ -2134,18 +2128,25 @@ var VALID_MERGE_STRATEGY = /* @__PURE__ */ new Set(["rebase", "merge-commit"]);
|
|
|
2134
2128
|
var VALID_EPIC_STRATEGY = /* @__PURE__ */ new Set(["parallel", "sequential"]);
|
|
2135
2129
|
var VALID_STORY_STRATEGY = /* @__PURE__ */ new Set(["sequential", "parallel"]);
|
|
2136
2130
|
function resolveHierarchicalFlow(parsed, resolvedTasks) {
|
|
2137
|
-
const hasFlow = "flow" in parsed && parsed.flow !== void 0;
|
|
2138
2131
|
const hasStoryFlow = "story_flow" in parsed && parsed.story_flow !== void 0;
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2132
|
+
const hasEpicFlow = "epic_flow" in parsed && parsed.epic_flow !== void 0;
|
|
2133
|
+
if (!hasStoryFlow) {
|
|
2134
|
+
throw new HierarchicalFlowError('Workflow must define "story_flow"');
|
|
2135
|
+
}
|
|
2136
|
+
if (!hasEpicFlow) {
|
|
2137
|
+
throw new HierarchicalFlowError('Workflow must define "epic_flow"');
|
|
2143
2138
|
}
|
|
2144
2139
|
const rawExecution = parsed.execution != null && typeof parsed.execution === "object" ? parsed.execution : {};
|
|
2145
2140
|
const execution = resolveExecutionConfig(rawExecution);
|
|
2146
|
-
const
|
|
2147
|
-
const storyFlow = normalizeFlowArray(rawStoryFlow);
|
|
2141
|
+
const storyFlow = normalizeFlowArray(parsed.story_flow);
|
|
2148
2142
|
const epicFlow = normalizeFlowArray(parsed.epic_flow);
|
|
2143
|
+
const storyFlowRefs = epicFlow.filter((s) => s === "story_flow");
|
|
2144
|
+
if (storyFlowRefs.length === 0) {
|
|
2145
|
+
throw new HierarchicalFlowError('epic_flow must contain a "story_flow" reference');
|
|
2146
|
+
}
|
|
2147
|
+
if (storyFlowRefs.length > 1) {
|
|
2148
|
+
throw new HierarchicalFlowError('epic_flow must contain exactly one "story_flow" reference');
|
|
2149
|
+
}
|
|
2149
2150
|
return {
|
|
2150
2151
|
execution,
|
|
2151
2152
|
storyFlow,
|
|
@@ -2286,23 +2287,20 @@ function validateAndResolve(parsed) {
|
|
|
2286
2287
|
);
|
|
2287
2288
|
}
|
|
2288
2289
|
const data = parsed;
|
|
2289
|
-
const hasFlow = "flow" in data && data.flow !== void 0;
|
|
2290
2290
|
const hasStoryFlow = "story_flow" in data && data.story_flow !== void 0;
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
[{ path: "/", message: 'Cannot have both "flow" and "story_flow"' }]
|
|
2295
|
-
);
|
|
2291
|
+
const hasEpicFlow = "epic_flow" in data && data.epic_flow !== void 0;
|
|
2292
|
+
if (!hasStoryFlow) {
|
|
2293
|
+
throw new WorkflowParseError('Workflow must define "story_flow"', [{ path: "/", message: 'Missing "story_flow"' }]);
|
|
2296
2294
|
}
|
|
2297
|
-
|
|
2295
|
+
if (!hasEpicFlow) {
|
|
2296
|
+
throw new WorkflowParseError('Workflow must define "epic_flow"', [{ path: "/", message: 'Missing "epic_flow"' }]);
|
|
2297
|
+
}
|
|
2298
|
+
const effectiveStoryFlow = data.story_flow ?? [];
|
|
2298
2299
|
const effectiveEpicFlow = data.epic_flow ?? [];
|
|
2299
2300
|
const taskNames = new Set(Object.keys(data.tasks));
|
|
2300
2301
|
const allErrors = [];
|
|
2301
|
-
|
|
2302
|
-
validateFlowReferences(
|
|
2303
|
-
if (effectiveEpicFlow.length > 0) {
|
|
2304
|
-
validateFlowReferences(effectiveEpicFlow, taskNames, "epic_flow", allErrors, true);
|
|
2305
|
-
}
|
|
2302
|
+
validateFlowReferences(effectiveStoryFlow, taskNames, "story_flow", allErrors, false);
|
|
2303
|
+
validateFlowReferences(effectiveEpicFlow, taskNames, "epic_flow", allErrors, true);
|
|
2306
2304
|
validateReferentialIntegrity(data, allErrors);
|
|
2307
2305
|
if (allErrors.length > 0) {
|
|
2308
2306
|
const details = allErrors.map((e) => e.message).join("; ");
|
|
@@ -2312,7 +2310,6 @@ function validateAndResolve(parsed) {
|
|
|
2312
2310
|
for (const [taskName, task] of Object.entries(data.tasks)) {
|
|
2313
2311
|
const resolved = {
|
|
2314
2312
|
agent: task.agent,
|
|
2315
|
-
scope: task.scope ?? "per-story",
|
|
2316
2313
|
session: task.session ?? "fresh",
|
|
2317
2314
|
source_access: task.source_access ?? true
|
|
2318
2315
|
};
|
|
@@ -2348,13 +2345,13 @@ function validateAndResolve(parsed) {
|
|
|
2348
2345
|
}
|
|
2349
2346
|
throw err;
|
|
2350
2347
|
}
|
|
2351
|
-
const resolvedFlow = hierarchical.storyFlow;
|
|
2352
2348
|
return {
|
|
2353
2349
|
tasks: resolvedTasks,
|
|
2354
|
-
flow: resolvedFlow,
|
|
2355
|
-
execution: hierarchical.execution,
|
|
2356
2350
|
storyFlow: hierarchical.storyFlow,
|
|
2357
|
-
epicFlow: hierarchical.epicFlow
|
|
2351
|
+
epicFlow: hierarchical.epicFlow,
|
|
2352
|
+
execution: hierarchical.execution,
|
|
2353
|
+
flow: hierarchical.storyFlow
|
|
2354
|
+
// deprecated compat
|
|
2358
2355
|
};
|
|
2359
2356
|
}
|
|
2360
2357
|
function validateFlowReferences(flow, taskNames, flowLabel, errors, allowBuiltins) {
|
|
@@ -3561,7 +3558,7 @@ function getFailedItems(verdict, allItems) {
|
|
|
3561
3558
|
if (verdict.verdict === "pass") return [];
|
|
3562
3559
|
return allItems;
|
|
3563
3560
|
}
|
|
3564
|
-
async function executeLoopBlock(loopBlock, state, config, workItems, initialContract) {
|
|
3561
|
+
async function executeLoopBlock(loopBlock, state, config, workItems, initialContract, storyFlowTasks) {
|
|
3565
3562
|
const projectDir = config.projectDir ?? process.cwd();
|
|
3566
3563
|
const maxIterations = config.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
3567
3564
|
const errors = [];
|
|
@@ -3578,7 +3575,7 @@ async function executeLoopBlock(loopBlock, state, config, workItems, initialCont
|
|
|
3578
3575
|
const allCurrentIterationDone = currentState.iteration > 0 && loopBlock.loop.every((tn) => {
|
|
3579
3576
|
const t = config.workflow.tasks[tn];
|
|
3580
3577
|
if (!t) return true;
|
|
3581
|
-
if (
|
|
3578
|
+
if (storyFlowTasks?.has(tn)) {
|
|
3582
3579
|
return workItems.every((item) => isLoopTaskCompleted(currentState, tn, item.key, currentState.iteration));
|
|
3583
3580
|
}
|
|
3584
3581
|
return isLoopTaskCompleted(currentState, tn, PER_RUN_SENTINEL, currentState.iteration);
|
|
@@ -3598,7 +3595,7 @@ async function executeLoopBlock(loopBlock, state, config, workItems, initialCont
|
|
|
3598
3595
|
continue;
|
|
3599
3596
|
}
|
|
3600
3597
|
if (task.agent === null) {
|
|
3601
|
-
if (
|
|
3598
|
+
if (storyFlowTasks?.has(taskName)) {
|
|
3602
3599
|
const itemsToProcess = lastVerdict ? getFailedItems(lastVerdict, workItems) : workItems;
|
|
3603
3600
|
for (const item of itemsToProcess) {
|
|
3604
3601
|
if (isLoopTaskCompleted(currentState, taskName, item.key, currentState.iteration)) {
|
|
@@ -3657,7 +3654,7 @@ async function executeLoopBlock(loopBlock, state, config, workItems, initialCont
|
|
|
3657
3654
|
warn(`workflow-engine: agent "${task.agent}" not found for task "${taskName}", skipping`);
|
|
3658
3655
|
continue;
|
|
3659
3656
|
}
|
|
3660
|
-
if (
|
|
3657
|
+
if (storyFlowTasks?.has(taskName)) {
|
|
3661
3658
|
const itemsToRetry = lastVerdict ? getFailedItems(lastVerdict, workItems) : workItems;
|
|
3662
3659
|
for (const item of itemsToRetry) {
|
|
3663
3660
|
if (isLoopTaskCompleted(currentState, taskName, item.key, currentState.iteration)) {
|
|
@@ -3911,298 +3908,125 @@ async function executeWorkflow(config) {
|
|
|
3911
3908
|
warn(cw.message);
|
|
3912
3909
|
}
|
|
3913
3910
|
const workItems = loadWorkItems(config.sprintStatusPath, config.issuesPath);
|
|
3911
|
+
const storyFlowTasks = /* @__PURE__ */ new Set();
|
|
3912
|
+
for (const step of config.workflow.storyFlow) {
|
|
3913
|
+
if (typeof step === "string") storyFlowTasks.add(step);
|
|
3914
|
+
}
|
|
3914
3915
|
let halted = false;
|
|
3915
3916
|
let lastOutputContract = null;
|
|
3916
3917
|
let accumulatedCostUsd = 0;
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
}
|
|
3926
|
-
if (typeof s === "string") preTasks.push(s);
|
|
3918
|
+
for (const step of config.workflow.epicFlow) {
|
|
3919
|
+
if (halted) break;
|
|
3920
|
+
if (config.abortSignal?.aborted) {
|
|
3921
|
+
info("Execution interrupted \u2014 saving state");
|
|
3922
|
+
state = { ...state, phase: "interrupted" };
|
|
3923
|
+
writeWorkflowState(state, projectDir);
|
|
3924
|
+
halted = true;
|
|
3925
|
+
break;
|
|
3927
3926
|
}
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
if (config.abortSignal?.aborted) {
|
|
3931
|
-
info("Execution interrupted \u2014 saving state");
|
|
3932
|
-
state = { ...state, phase: "interrupted" };
|
|
3933
|
-
writeWorkflowState(state, projectDir);
|
|
3934
|
-
}
|
|
3935
|
-
halted = true;
|
|
3936
|
-
break;
|
|
3937
|
-
}
|
|
3938
|
-
processedStories.add(item.key);
|
|
3939
|
-
for (const taskName of preTasks) {
|
|
3927
|
+
if (step === "story_flow") {
|
|
3928
|
+
for (const item of workItems) {
|
|
3940
3929
|
if (halted || config.abortSignal?.aborted) {
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
}
|
|
3944
|
-
const task = config.workflow.tasks[taskName];
|
|
3945
|
-
if (!task || task.agent === null) continue;
|
|
3946
|
-
const definition = config.agents[task.agent];
|
|
3947
|
-
if (!definition) {
|
|
3948
|
-
warn(`workflow-engine: agent "${task.agent}" not found for "${taskName}"`);
|
|
3949
|
-
continue;
|
|
3950
|
-
}
|
|
3951
|
-
if (isTaskCompleted(state, taskName, item.key)) continue;
|
|
3952
|
-
try {
|
|
3953
|
-
const dr = await dispatchTaskWithResult(task, taskName, item.key, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
3954
|
-
state = dr.updatedState;
|
|
3955
|
-
lastOutputContract = dr.contract;
|
|
3956
|
-
propagateVerifyFlags(taskName, dr.contract, projectDir);
|
|
3957
|
-
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
3958
|
-
tasksCompleted++;
|
|
3959
|
-
} catch (err) {
|
|
3960
|
-
const engineError = handleDispatchError(err, taskName, item.key);
|
|
3961
|
-
errors.push(engineError);
|
|
3962
|
-
if (config.onEvent) {
|
|
3963
|
-
config.onEvent({ type: "dispatch-error", taskName, storyKey: item.key, error: { code: engineError.code, message: engineError.message } });
|
|
3964
|
-
} else {
|
|
3965
|
-
warn(`[${taskName}] ${item.key} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
3966
|
-
}
|
|
3967
|
-
state = recordErrorInState(state, taskName, item.key, engineError);
|
|
3968
|
-
writeWorkflowState(state, projectDir);
|
|
3969
|
-
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
3970
|
-
halted = true;
|
|
3971
|
-
}
|
|
3972
|
-
break;
|
|
3973
|
-
}
|
|
3974
|
-
}
|
|
3975
|
-
}
|
|
3976
|
-
const remaining = loopIdx >= 0 ? config.workflow.storyFlow.slice(loopIdx) : [];
|
|
3977
|
-
for (const step of remaining) {
|
|
3978
|
-
if (halted || config.abortSignal?.aborted) {
|
|
3979
|
-
halted = true;
|
|
3980
|
-
break;
|
|
3981
|
-
}
|
|
3982
|
-
if (isLoopBlock(step)) {
|
|
3983
|
-
const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract);
|
|
3984
|
-
state = loopResult.state;
|
|
3985
|
-
errors.push(...loopResult.errors);
|
|
3986
|
-
tasksCompleted += loopResult.tasksCompleted;
|
|
3987
|
-
lastOutputContract = loopResult.lastContract;
|
|
3988
|
-
for (const item of workItems) processedStories.add(item.key);
|
|
3989
|
-
if (loopResult.halted || state.phase === "max-iterations" || state.phase === "circuit-breaker") {
|
|
3990
|
-
halted = true;
|
|
3991
|
-
}
|
|
3992
|
-
continue;
|
|
3993
|
-
}
|
|
3994
|
-
const taskName = step;
|
|
3995
|
-
const task = config.workflow.tasks[taskName];
|
|
3996
|
-
if (!task) continue;
|
|
3997
|
-
if (task.agent === null) continue;
|
|
3998
|
-
const definition = config.agents[task.agent];
|
|
3999
|
-
if (!definition) continue;
|
|
4000
|
-
if (task.scope === "per-run" || task.scope === "per-epic") {
|
|
4001
|
-
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) continue;
|
|
4002
|
-
try {
|
|
4003
|
-
const dr = await dispatchTaskWithResult(task, taskName, PER_RUN_SENTINEL, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
4004
|
-
state = dr.updatedState;
|
|
4005
|
-
lastOutputContract = dr.contract;
|
|
4006
|
-
tasksCompleted++;
|
|
4007
|
-
} catch (err) {
|
|
4008
|
-
const engineError = handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
4009
|
-
errors.push(engineError);
|
|
4010
|
-
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
4011
|
-
writeWorkflowState(state, projectDir);
|
|
4012
|
-
}
|
|
4013
|
-
} else {
|
|
4014
|
-
for (const item of workItems) {
|
|
4015
|
-
if (halted || config.abortSignal?.aborted) break;
|
|
4016
|
-
if (isTaskCompleted(state, taskName, item.key)) continue;
|
|
4017
|
-
try {
|
|
4018
|
-
const dr = await dispatchTaskWithResult(task, taskName, item.key, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
4019
|
-
state = dr.updatedState;
|
|
4020
|
-
lastOutputContract = dr.contract;
|
|
4021
|
-
tasksCompleted++;
|
|
4022
|
-
} catch (err) {
|
|
4023
|
-
const engineError = handleDispatchError(err, taskName, item.key);
|
|
4024
|
-
errors.push(engineError);
|
|
4025
|
-
state = recordErrorInState(state, taskName, item.key, engineError);
|
|
3930
|
+
if (config.abortSignal?.aborted) {
|
|
3931
|
+
state = { ...state, phase: "interrupted" };
|
|
4026
3932
|
writeWorkflowState(state, projectDir);
|
|
4027
3933
|
}
|
|
4028
|
-
}
|
|
4029
|
-
}
|
|
4030
|
-
}
|
|
4031
|
-
}
|
|
4032
|
-
if (!config.storyPipeline)
|
|
4033
|
-
for (const step of config.workflow.storyFlow) {
|
|
4034
|
-
if (halted) break;
|
|
4035
|
-
if (config.abortSignal?.aborted) {
|
|
4036
|
-
info("Execution interrupted \u2014 saving state");
|
|
4037
|
-
state = { ...state, phase: "interrupted" };
|
|
4038
|
-
writeWorkflowState(state, projectDir);
|
|
4039
|
-
halted = true;
|
|
4040
|
-
break;
|
|
4041
|
-
}
|
|
4042
|
-
if (isLoopBlock(step)) {
|
|
4043
|
-
const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract);
|
|
4044
|
-
state = loopResult.state;
|
|
4045
|
-
errors.push(...loopResult.errors);
|
|
4046
|
-
tasksCompleted += loopResult.tasksCompleted;
|
|
4047
|
-
lastOutputContract = loopResult.lastContract;
|
|
4048
|
-
for (const item of workItems) {
|
|
4049
|
-
processedStories.add(item.key);
|
|
4050
|
-
}
|
|
4051
|
-
if (loopResult.halted) {
|
|
4052
|
-
halted = true;
|
|
4053
|
-
}
|
|
4054
|
-
if (state.phase === "max-iterations" || state.phase === "circuit-breaker") {
|
|
4055
3934
|
halted = true;
|
|
3935
|
+
break;
|
|
4056
3936
|
}
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
const task = config.workflow.tasks[taskName];
|
|
4061
|
-
if (!task) {
|
|
4062
|
-
warn(`workflow-engine: task "${taskName}" not found in workflow tasks, skipping`);
|
|
4063
|
-
continue;
|
|
4064
|
-
}
|
|
4065
|
-
if (task.agent === null) {
|
|
4066
|
-
if (task.scope === "per-run") {
|
|
4067
|
-
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) {
|
|
4068
|
-
warn(`workflow-engine: skipping completed task ${taskName} for ${PER_RUN_SENTINEL}`);
|
|
4069
|
-
continue;
|
|
4070
|
-
}
|
|
4071
|
-
try {
|
|
4072
|
-
const nullResult = await executeNullTask(
|
|
4073
|
-
task,
|
|
4074
|
-
taskName,
|
|
4075
|
-
PER_RUN_SENTINEL,
|
|
4076
|
-
state,
|
|
4077
|
-
config,
|
|
4078
|
-
lastOutputContract ?? void 0,
|
|
4079
|
-
accumulatedCostUsd
|
|
4080
|
-
);
|
|
4081
|
-
state = nullResult.updatedState;
|
|
4082
|
-
lastOutputContract = nullResult.contract;
|
|
4083
|
-
tasksCompleted++;
|
|
4084
|
-
} catch (err) {
|
|
4085
|
-
const engineError = isEngineError(err) ? err : handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
4086
|
-
errors.push(engineError);
|
|
4087
|
-
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
4088
|
-
writeWorkflowState(state, projectDir);
|
|
4089
|
-
}
|
|
4090
|
-
} else {
|
|
4091
|
-
for (const item of workItems) {
|
|
4092
|
-
processedStories.add(item.key);
|
|
4093
|
-
if (isTaskCompleted(state, taskName, item.key)) {
|
|
4094
|
-
warn(`workflow-engine: skipping completed task ${taskName} for ${item.key}`);
|
|
4095
|
-
continue;
|
|
4096
|
-
}
|
|
4097
|
-
try {
|
|
4098
|
-
const nullResult = await executeNullTask(
|
|
4099
|
-
task,
|
|
4100
|
-
taskName,
|
|
4101
|
-
item.key,
|
|
4102
|
-
state,
|
|
4103
|
-
config,
|
|
4104
|
-
lastOutputContract ?? void 0,
|
|
4105
|
-
accumulatedCostUsd
|
|
4106
|
-
);
|
|
4107
|
-
state = nullResult.updatedState;
|
|
4108
|
-
lastOutputContract = nullResult.contract;
|
|
4109
|
-
tasksCompleted++;
|
|
4110
|
-
} catch (err) {
|
|
4111
|
-
const engineError = isEngineError(err) ? err : handleDispatchError(err, taskName, item.key);
|
|
4112
|
-
errors.push(engineError);
|
|
4113
|
-
state = recordErrorInState(state, taskName, item.key, engineError);
|
|
4114
|
-
writeWorkflowState(state, projectDir);
|
|
4115
|
-
}
|
|
4116
|
-
}
|
|
4117
|
-
}
|
|
4118
|
-
continue;
|
|
4119
|
-
}
|
|
4120
|
-
const definition = config.agents[task.agent];
|
|
4121
|
-
if (!definition) {
|
|
4122
|
-
warn(`workflow-engine: agent "${task.agent}" not found for task "${taskName}", skipping`);
|
|
4123
|
-
continue;
|
|
4124
|
-
}
|
|
4125
|
-
if (task.scope === "per-run") {
|
|
4126
|
-
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) {
|
|
4127
|
-
warn(`workflow-engine: skipping completed task ${taskName} for ${PER_RUN_SENTINEL}`);
|
|
4128
|
-
continue;
|
|
4129
|
-
}
|
|
4130
|
-
try {
|
|
4131
|
-
const dispatchResult = await dispatchTaskWithResult(
|
|
4132
|
-
task,
|
|
4133
|
-
taskName,
|
|
4134
|
-
PER_RUN_SENTINEL,
|
|
4135
|
-
definition,
|
|
4136
|
-
state,
|
|
4137
|
-
config,
|
|
4138
|
-
void 0,
|
|
4139
|
-
lastOutputContract ?? void 0
|
|
4140
|
-
);
|
|
4141
|
-
state = dispatchResult.updatedState;
|
|
4142
|
-
lastOutputContract = dispatchResult.contract;
|
|
4143
|
-
propagateVerifyFlags(taskName, dispatchResult.contract, projectDir);
|
|
4144
|
-
accumulatedCostUsd += dispatchResult.contract?.cost_usd ?? 0;
|
|
4145
|
-
tasksCompleted++;
|
|
4146
|
-
} catch (err) {
|
|
4147
|
-
const engineError = handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
4148
|
-
errors.push(engineError);
|
|
4149
|
-
if (config.onEvent) {
|
|
4150
|
-
config.onEvent({ type: "dispatch-error", taskName, storyKey: PER_RUN_SENTINEL, error: { code: engineError.code, message: engineError.message } });
|
|
4151
|
-
} else {
|
|
4152
|
-
warn(`[${taskName}] ${PER_RUN_SENTINEL} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4153
|
-
}
|
|
4154
|
-
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
4155
|
-
writeWorkflowState(state, projectDir);
|
|
4156
|
-
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
4157
|
-
halted = true;
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
4160
|
-
} else {
|
|
4161
|
-
for (const item of workItems) {
|
|
4162
|
-
if (config.abortSignal?.aborted) {
|
|
3937
|
+
processedStories.add(item.key);
|
|
3938
|
+
for (const storyStep of config.workflow.storyFlow) {
|
|
3939
|
+
if (halted || config.abortSignal?.aborted) {
|
|
4163
3940
|
halted = true;
|
|
4164
3941
|
break;
|
|
4165
3942
|
}
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
3943
|
+
if (typeof storyStep !== "string") continue;
|
|
3944
|
+
const taskName2 = storyStep;
|
|
3945
|
+
const task2 = config.workflow.tasks[taskName2];
|
|
3946
|
+
if (!task2) {
|
|
3947
|
+
warn(`workflow-engine: task "${taskName2}" not found, skipping`);
|
|
4169
3948
|
continue;
|
|
4170
3949
|
}
|
|
3950
|
+
if (task2.agent === null) continue;
|
|
3951
|
+
const definition2 = config.agents[task2.agent];
|
|
3952
|
+
if (!definition2) {
|
|
3953
|
+
warn(`workflow-engine: agent "${task2.agent}" not found for "${taskName2}"`);
|
|
3954
|
+
continue;
|
|
3955
|
+
}
|
|
3956
|
+
if (isTaskCompleted(state, taskName2, item.key)) continue;
|
|
4171
3957
|
try {
|
|
4172
|
-
const
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
state,
|
|
4178
|
-
config,
|
|
4179
|
-
void 0,
|
|
4180
|
-
lastOutputContract ?? void 0
|
|
4181
|
-
);
|
|
4182
|
-
state = dispatchResult.updatedState;
|
|
4183
|
-
lastOutputContract = dispatchResult.contract;
|
|
4184
|
-
propagateVerifyFlags(taskName, dispatchResult.contract, projectDir);
|
|
4185
|
-
accumulatedCostUsd += dispatchResult.contract?.cost_usd ?? 0;
|
|
3958
|
+
const dr = await dispatchTaskWithResult(task2, taskName2, item.key, definition2, state, config, void 0, lastOutputContract ?? void 0);
|
|
3959
|
+
state = dr.updatedState;
|
|
3960
|
+
lastOutputContract = dr.contract;
|
|
3961
|
+
propagateVerifyFlags(taskName2, dr.contract, projectDir);
|
|
3962
|
+
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
4186
3963
|
tasksCompleted++;
|
|
4187
3964
|
} catch (err) {
|
|
4188
|
-
const engineError = handleDispatchError(err,
|
|
3965
|
+
const engineError = handleDispatchError(err, taskName2, item.key);
|
|
4189
3966
|
errors.push(engineError);
|
|
4190
3967
|
if (config.onEvent) {
|
|
4191
|
-
config.onEvent({ type: "dispatch-error", taskName, storyKey: item.key, error: { code: engineError.code, message: engineError.message } });
|
|
3968
|
+
config.onEvent({ type: "dispatch-error", taskName: taskName2, storyKey: item.key, error: { code: engineError.code, message: engineError.message } });
|
|
4192
3969
|
} else {
|
|
4193
|
-
warn(`[${
|
|
3970
|
+
warn(`[${taskName2}] ${item.key} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4194
3971
|
}
|
|
4195
|
-
state = recordErrorInState(state,
|
|
3972
|
+
state = recordErrorInState(state, taskName2, item.key, engineError);
|
|
4196
3973
|
writeWorkflowState(state, projectDir);
|
|
4197
3974
|
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
4198
3975
|
halted = true;
|
|
4199
|
-
break;
|
|
4200
3976
|
}
|
|
4201
|
-
|
|
3977
|
+
break;
|
|
4202
3978
|
}
|
|
4203
3979
|
}
|
|
4204
3980
|
}
|
|
3981
|
+
continue;
|
|
3982
|
+
}
|
|
3983
|
+
if (isLoopBlock(step)) {
|
|
3984
|
+
const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract, storyFlowTasks);
|
|
3985
|
+
state = loopResult.state;
|
|
3986
|
+
errors.push(...loopResult.errors);
|
|
3987
|
+
tasksCompleted += loopResult.tasksCompleted;
|
|
3988
|
+
lastOutputContract = loopResult.lastContract;
|
|
3989
|
+
for (const item of workItems) processedStories.add(item.key);
|
|
3990
|
+
if (loopResult.halted || state.phase === "max-iterations" || state.phase === "circuit-breaker") {
|
|
3991
|
+
halted = true;
|
|
3992
|
+
}
|
|
3993
|
+
continue;
|
|
3994
|
+
}
|
|
3995
|
+
const taskName = step;
|
|
3996
|
+
const task = config.workflow.tasks[taskName];
|
|
3997
|
+
if (!task) {
|
|
3998
|
+
warn(`workflow-engine: task "${taskName}" not found, skipping`);
|
|
3999
|
+
continue;
|
|
4000
|
+
}
|
|
4001
|
+
if (task.agent === null) continue;
|
|
4002
|
+
const definition = config.agents[task.agent];
|
|
4003
|
+
if (!definition) {
|
|
4004
|
+
warn(`workflow-engine: agent "${task.agent}" not found for "${taskName}"`);
|
|
4005
|
+
continue;
|
|
4205
4006
|
}
|
|
4007
|
+
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) continue;
|
|
4008
|
+
try {
|
|
4009
|
+
const dr = await dispatchTaskWithResult(task, taskName, PER_RUN_SENTINEL, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
4010
|
+
state = dr.updatedState;
|
|
4011
|
+
lastOutputContract = dr.contract;
|
|
4012
|
+
propagateVerifyFlags(taskName, dr.contract, projectDir);
|
|
4013
|
+
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
4014
|
+
tasksCompleted++;
|
|
4015
|
+
} catch (err) {
|
|
4016
|
+
const engineError = handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
4017
|
+
errors.push(engineError);
|
|
4018
|
+
if (config.onEvent) {
|
|
4019
|
+
config.onEvent({ type: "dispatch-error", taskName, storyKey: PER_RUN_SENTINEL, error: { code: engineError.code, message: engineError.message } });
|
|
4020
|
+
} else {
|
|
4021
|
+
warn(`[${taskName}] ${PER_RUN_SENTINEL} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4022
|
+
}
|
|
4023
|
+
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
4024
|
+
writeWorkflowState(state, projectDir);
|
|
4025
|
+
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
4026
|
+
halted = true;
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4206
4030
|
if (state.phase === "interrupted") {
|
|
4207
4031
|
} else if (errors.length === 0 && state.phase !== "max-iterations" && state.phase !== "circuit-breaker") {
|
|
4208
4032
|
state = { ...state, phase: "completed" };
|
|
@@ -5217,9 +5041,13 @@ function WorkflowGraph({ flow, currentTask, taskStates, taskMeta }) {
|
|
|
5217
5041
|
if (isLoopBlock2(step)) break;
|
|
5218
5042
|
if (typeof step === "string") {
|
|
5219
5043
|
if (elements2.length > 0) elements2.push(/* @__PURE__ */ jsx2(Text2, { children: " \u2192 " }, `a-${step}`));
|
|
5220
|
-
|
|
5221
|
-
/* @__PURE__ */ jsx2(
|
|
5222
|
-
|
|
5044
|
+
if (step === "story_flow") {
|
|
5045
|
+
elements2.push(/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "stories \u2713" }, "sf"));
|
|
5046
|
+
} else {
|
|
5047
|
+
elements2.push(
|
|
5048
|
+
/* @__PURE__ */ jsx2(TaskNode, { name: step, status: taskStates[step], spinnerFrame, driver: meta[step]?.driver }, `t-${step}`)
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5223
5051
|
}
|
|
5224
5052
|
}
|
|
5225
5053
|
if (elements2.length > 0) elements2.push(/* @__PURE__ */ jsx2(Text2, { children: " \u2192 " }, "loop-arrow"));
|
|
@@ -5246,9 +5074,13 @@ function WorkflowGraph({ flow, currentTask, taskStates, taskMeta }) {
|
|
|
5246
5074
|
if (elements.length > 0) {
|
|
5247
5075
|
elements.push(/* @__PURE__ */ jsx2(Text2, { children: " \u2192 " }, `a-${step}`));
|
|
5248
5076
|
}
|
|
5249
|
-
|
|
5250
|
-
/* @__PURE__ */ jsx2(
|
|
5251
|
-
|
|
5077
|
+
if (step === "story_flow") {
|
|
5078
|
+
elements.push(/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "stories \u2713" }, "sf"));
|
|
5079
|
+
} else {
|
|
5080
|
+
elements.push(
|
|
5081
|
+
/* @__PURE__ */ jsx2(TaskNode, { name: step, status: taskStates[step], spinnerFrame, driver: meta[step]?.driver }, `t-${step}`)
|
|
5082
|
+
);
|
|
5083
|
+
}
|
|
5252
5084
|
}
|
|
5253
5085
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
5254
5086
|
" ",
|
|
@@ -6288,32 +6120,25 @@ function registerRunCommand(program) {
|
|
|
6288
6120
|
epicData[epicId] = { storiesDone: epic.storiesDone ?? 0, storiesTotal: epic.storiesTotal ?? 0 };
|
|
6289
6121
|
}
|
|
6290
6122
|
}
|
|
6291
|
-
const
|
|
6292
|
-
const
|
|
6293
|
-
|
|
6294
|
-
if (typeof step === "string") {
|
|
6295
|
-
preLoopTasks.add(step);
|
|
6296
|
-
} else if (typeof step === "object" && "loop" in step) {
|
|
6297
|
-
for (const lt of step.loop) loopTasks.add(lt);
|
|
6298
|
-
}
|
|
6123
|
+
const storyFlowTasks = /* @__PURE__ */ new Set();
|
|
6124
|
+
for (const step of parsedWorkflow.storyFlow) {
|
|
6125
|
+
if (typeof step === "string") storyFlowTasks.add(step);
|
|
6299
6126
|
}
|
|
6300
|
-
|
|
6301
|
-
for (const step of parsedWorkflow.
|
|
6127
|
+
const epicLoopTasks = /* @__PURE__ */ new Set();
|
|
6128
|
+
for (const step of parsedWorkflow.epicFlow) {
|
|
6302
6129
|
if (typeof step === "object" && "loop" in step) {
|
|
6303
|
-
|
|
6304
|
-
continue;
|
|
6130
|
+
for (const lt of step.loop) epicLoopTasks.add(lt);
|
|
6305
6131
|
}
|
|
6306
|
-
if (pastLoop && typeof step === "string") preLoopTasks.add(step);
|
|
6307
6132
|
}
|
|
6308
|
-
let
|
|
6133
|
+
let inEpicPhase = false;
|
|
6309
6134
|
const taskStates = {};
|
|
6310
6135
|
const taskMeta = {};
|
|
6311
6136
|
for (const [tn, task] of Object.entries(parsedWorkflow.tasks)) {
|
|
6312
6137
|
taskStates[tn] = "pending";
|
|
6313
|
-
if (
|
|
6138
|
+
if (epicLoopTasks.has(tn)) taskStates[`loop:${tn}`] = "pending";
|
|
6314
6139
|
const driverLabel2 = task.model ?? task.driver ?? "claude-code";
|
|
6315
6140
|
taskMeta[tn] = { driver: driverLabel2 };
|
|
6316
|
-
if (
|
|
6141
|
+
if (epicLoopTasks.has(tn)) taskMeta[`loop:${tn}`] = { driver: driverLabel2 };
|
|
6317
6142
|
}
|
|
6318
6143
|
const storyEntries = [];
|
|
6319
6144
|
for (const [key, status] of Object.entries(statuses)) {
|
|
@@ -6324,24 +6149,27 @@ function registerRunCommand(program) {
|
|
|
6324
6149
|
else if (status === "failed") storyEntries.push({ key, status: "failed" });
|
|
6325
6150
|
}
|
|
6326
6151
|
renderer.updateStories(storyEntries);
|
|
6327
|
-
renderer.updateWorkflowState(parsedWorkflow.
|
|
6152
|
+
renderer.updateWorkflowState(parsedWorkflow.storyFlow, null, { ...taskStates }, { ...taskMeta });
|
|
6328
6153
|
const onEvent = (event) => {
|
|
6329
6154
|
if (event.type === "stream-event" && event.streamEvent) {
|
|
6330
6155
|
renderer.update(event.streamEvent, event.driverName);
|
|
6331
6156
|
}
|
|
6332
6157
|
if (event.type === "dispatch-start") {
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6158
|
+
const isStoryTask = storyFlowTasks.has(event.taskName);
|
|
6159
|
+
const isEpicTask = !isStoryTask;
|
|
6160
|
+
if (isStoryTask && event.storyKey !== currentStoryKey) {
|
|
6161
|
+
inEpicPhase = false;
|
|
6162
|
+
for (const tn of storyFlowTasks) {
|
|
6336
6163
|
taskStates[tn] = "pending";
|
|
6337
6164
|
}
|
|
6338
6165
|
}
|
|
6166
|
+
if (isEpicTask && !inEpicPhase) {
|
|
6167
|
+
inEpicPhase = true;
|
|
6168
|
+
}
|
|
6339
6169
|
currentStoryKey = event.storyKey;
|
|
6340
6170
|
currentTaskName = event.taskName;
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
}
|
|
6344
|
-
const stateKey = inLoop && loopTasks.has(event.taskName) ? `loop:${event.taskName}` : event.taskName;
|
|
6171
|
+
const inLoop = inEpicPhase && epicLoopTasks.has(event.taskName) && taskStates[event.taskName] === "done";
|
|
6172
|
+
const stateKey = inLoop ? `loop:${event.taskName}` : event.taskName;
|
|
6345
6173
|
const epicId = extractEpicId2(event.storyKey);
|
|
6346
6174
|
const epic = epicData[epicId];
|
|
6347
6175
|
renderer.updateSprintState({
|
|
@@ -6356,33 +6184,29 @@ function registerRunCommand(program) {
|
|
|
6356
6184
|
epicStoriesTotal: epic?.storiesTotal ?? 0
|
|
6357
6185
|
});
|
|
6358
6186
|
taskStates[stateKey] = "active";
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6187
|
+
const displayFlow = inEpicPhase ? parsedWorkflow.epicFlow : parsedWorkflow.storyFlow;
|
|
6188
|
+
renderer.updateWorkflowState(displayFlow, event.taskName, { ...taskStates }, { ...taskMeta });
|
|
6189
|
+
if (isStoryTask) {
|
|
6190
|
+
updateStoryStatus2(event.storyKey, "in-progress");
|
|
6191
|
+
const idx = storyEntries.findIndex((s) => s.key === event.storyKey);
|
|
6192
|
+
if (idx >= 0 && storyEntries[idx].status === "pending") {
|
|
6193
|
+
storyEntries[idx] = { ...storyEntries[idx], status: "in-progress" };
|
|
6194
|
+
renderer.updateStories([...storyEntries]);
|
|
6195
|
+
}
|
|
6365
6196
|
}
|
|
6366
6197
|
}
|
|
6367
6198
|
if (event.type === "dispatch-end") {
|
|
6368
6199
|
totalCostUsd += event.costUsd ?? 0;
|
|
6369
|
-
const
|
|
6200
|
+
const inLoop = inEpicPhase && epicLoopTasks.has(event.taskName) && taskStates[event.taskName] === "done";
|
|
6201
|
+
const stateKey = inLoop ? `loop:${event.taskName}` : event.taskName;
|
|
6370
6202
|
taskStates[stateKey] = "done";
|
|
6371
6203
|
taskMeta[stateKey] = {
|
|
6372
6204
|
...taskMeta[stateKey],
|
|
6373
6205
|
costUsd: (taskMeta[stateKey]?.costUsd ?? 0) + (event.costUsd ?? 0),
|
|
6374
6206
|
elapsedMs: (taskMeta[stateKey]?.elapsedMs ?? 0) + (event.elapsedMs ?? 0)
|
|
6375
6207
|
};
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
storiesDone++;
|
|
6379
|
-
updateStoryStatus2(event.storyKey, "done");
|
|
6380
|
-
const idx = storyEntries.findIndex((s) => s.key === event.storyKey);
|
|
6381
|
-
if (idx >= 0) {
|
|
6382
|
-
storyEntries[idx] = { ...storyEntries[idx], status: "done" };
|
|
6383
|
-
renderer.updateStories([...storyEntries]);
|
|
6384
|
-
}
|
|
6385
|
-
}
|
|
6208
|
+
const displayFlow = inEpicPhase ? parsedWorkflow.epicFlow : parsedWorkflow.storyFlow;
|
|
6209
|
+
renderer.updateWorkflowState(displayFlow, event.taskName, { ...taskStates }, { ...taskMeta });
|
|
6386
6210
|
renderer.updateSprintState({
|
|
6387
6211
|
storyKey: event.storyKey,
|
|
6388
6212
|
phase: event.taskName,
|
|
@@ -6390,11 +6214,25 @@ function registerRunCommand(program) {
|
|
|
6390
6214
|
total: counts.total,
|
|
6391
6215
|
totalCost: totalCostUsd
|
|
6392
6216
|
});
|
|
6217
|
+
if (storyFlowTasks.has(event.taskName)) {
|
|
6218
|
+
const allStoryDone = [...storyFlowTasks].every((tn) => taskStates[tn] === "done");
|
|
6219
|
+
if (allStoryDone) {
|
|
6220
|
+
storiesDone++;
|
|
6221
|
+
updateStoryStatus2(event.storyKey, "done");
|
|
6222
|
+
const idx = storyEntries.findIndex((s) => s.key === event.storyKey);
|
|
6223
|
+
if (idx >= 0) {
|
|
6224
|
+
storyEntries[idx] = { ...storyEntries[idx], status: "done" };
|
|
6225
|
+
renderer.updateStories([...storyEntries]);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
6393
6229
|
}
|
|
6394
6230
|
if (event.type === "dispatch-error") {
|
|
6395
|
-
const
|
|
6231
|
+
const inLoop = inEpicPhase && epicLoopTasks.has(event.taskName);
|
|
6232
|
+
const stateKey = inLoop ? `loop:${event.taskName}` : event.taskName;
|
|
6396
6233
|
taskStates[stateKey] = "failed";
|
|
6397
|
-
|
|
6234
|
+
const displayFlow = inEpicPhase ? parsedWorkflow.epicFlow : parsedWorkflow.storyFlow;
|
|
6235
|
+
renderer.updateWorkflowState(displayFlow, event.taskName, { ...taskStates }, { ...taskMeta });
|
|
6398
6236
|
renderer.addMessage({
|
|
6399
6237
|
type: "fail",
|
|
6400
6238
|
key: event.storyKey,
|
|
@@ -6416,7 +6254,6 @@ function registerRunCommand(program) {
|
|
|
6416
6254
|
runId: `run-${Date.now()}`,
|
|
6417
6255
|
projectDir,
|
|
6418
6256
|
abortSignal: abortController.signal,
|
|
6419
|
-
storyPipeline: true,
|
|
6420
6257
|
maxIterations,
|
|
6421
6258
|
onEvent
|
|
6422
6259
|
};
|
|
@@ -11298,7 +11135,7 @@ function registerTeardownCommand(program) {
|
|
|
11298
11135
|
} else if (otlpMode === "remote-routed") {
|
|
11299
11136
|
if (!options.keepDocker) {
|
|
11300
11137
|
try {
|
|
11301
|
-
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-
|
|
11138
|
+
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-R4444RHQ.js");
|
|
11302
11139
|
stopCollectorOnly2();
|
|
11303
11140
|
result.docker.stopped = true;
|
|
11304
11141
|
if (!isJson) {
|
|
@@ -11330,7 +11167,7 @@ function registerTeardownCommand(program) {
|
|
|
11330
11167
|
info("Shared stack: kept running (other projects may use it)");
|
|
11331
11168
|
}
|
|
11332
11169
|
} else if (isLegacyStack) {
|
|
11333
|
-
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-
|
|
11170
|
+
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-R4444RHQ.js");
|
|
11334
11171
|
let stackRunning = false;
|
|
11335
11172
|
try {
|
|
11336
11173
|
stackRunning = isStackRunning2(composeFile);
|
|
@@ -14317,7 +14154,7 @@ function registerDriversCommand(program) {
|
|
|
14317
14154
|
}
|
|
14318
14155
|
|
|
14319
14156
|
// src/index.ts
|
|
14320
|
-
var VERSION = true ? "0.
|
|
14157
|
+
var VERSION = true ? "0.32.0" : "0.0.0-dev";
|
|
14321
14158
|
function createProgram() {
|
|
14322
14159
|
const program = new Command();
|
|
14323
14160
|
program.name("codeharness").description("Makes autonomous coding agents produce software that actually works").version(VERSION).option("--json", "Output in machine-readable JSON format");
|
package/package.json
CHANGED
|
@@ -1,52 +1,48 @@
|
|
|
1
1
|
tasks:
|
|
2
2
|
create-story:
|
|
3
3
|
agent: story-creator
|
|
4
|
-
scope: per-story
|
|
5
4
|
session: fresh
|
|
6
5
|
source_access: true
|
|
7
6
|
model: claude-opus-4-6
|
|
8
7
|
implement:
|
|
9
8
|
agent: dev
|
|
10
|
-
scope: per-story
|
|
11
9
|
session: fresh
|
|
12
10
|
source_access: true
|
|
13
11
|
model: claude-sonnet-4-6
|
|
14
12
|
check:
|
|
15
13
|
agent: checker
|
|
16
|
-
scope: per-story
|
|
17
14
|
session: fresh
|
|
18
15
|
source_access: true
|
|
19
16
|
driver: codex
|
|
20
17
|
review:
|
|
21
18
|
agent: reviewer
|
|
22
|
-
scope: per-story
|
|
23
19
|
session: fresh
|
|
24
20
|
source_access: true
|
|
25
21
|
driver: codex
|
|
26
22
|
verify:
|
|
27
23
|
agent: evaluator
|
|
28
|
-
scope: per-story
|
|
29
24
|
session: fresh
|
|
30
25
|
source_access: false
|
|
31
26
|
driver: codex
|
|
32
27
|
retry:
|
|
33
28
|
agent: dev
|
|
34
|
-
scope: per-story
|
|
35
29
|
session: fresh
|
|
36
30
|
source_access: true
|
|
37
31
|
model: claude-sonnet-4-6
|
|
38
32
|
retro:
|
|
39
33
|
agent: retro
|
|
40
|
-
scope: per-epic
|
|
41
34
|
session: fresh
|
|
42
35
|
source_access: true
|
|
43
36
|
model: claude-opus-4-6
|
|
44
37
|
|
|
45
|
-
|
|
38
|
+
story_flow:
|
|
46
39
|
- create-story
|
|
47
40
|
- implement
|
|
48
41
|
- check
|
|
49
42
|
- review
|
|
43
|
+
|
|
44
|
+
epic_flow:
|
|
45
|
+
- story_flow
|
|
50
46
|
- verify
|
|
51
47
|
- loop:
|
|
52
48
|
- retry
|