Skip to content

Commit bbeeb61

Browse files
committed
feat(workflows): add controller availability check for FSM guard
Add hasController flag to workflow context to distinguish between interactive and non-interactive steps Update FSM guard to allow non-interactive steps without controller Set controller state during runner initialization
1 parent c765ba9 commit bbeeb61

3 files changed

Lines changed: 17 additions & 3 deletions

File tree

src/workflows/runner/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,18 +215,24 @@ export class WorkflowRunner implements RunnerContext {
215215
async run(): Promise<void> {
216216
debug('[Runner] Starting workflow');
217217

218-
// Load initial auto mode state
218+
// Load initial auto mode and controller state
219219
// autoMode can work without controller for non-interactive steps (Scenarios 5-6)
220-
// For interactive steps without controller, controller provider delegates to user input
220+
// For interactive steps, controller is required to enter delegated state
221221
const controllerState = await loadControllerConfig(this.cmRoot);
222222
// Check for string 'true' or 'always' (autonomous mode)
223223
const isAutoMode = controllerState?.autonomousMode === 'true' || controllerState?.autonomousMode === 'always';
224+
const hasController = controllerState?.controllerConfig != null;
225+
224226
if (isAutoMode) {
225227
this.mode.enableAutoMode();
226228
// Sync machine context with mode state
227229
this.machine.context.autoMode = true;
228230
}
229231

232+
// Set controller availability for FSM guard
233+
this.machine.context.hasController = hasController;
234+
debug('[Runner] Controller state: autoMode=%s, hasController=%s', isAutoMode, hasController);
235+
230236
// Start the machine
231237
this.machine.send({ type: 'START' });
232238
this.emitter.setWorkflowStatus('running');

src/workflows/state/machine.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export function createWorkflowMachine(initialContext: Partial<WorkflowContext> =
124124
steps: [],
125125
currentOutput: null,
126126
autoMode: false,
127+
hasController: false,
127128
paused: false,
128129
cwd: process.cwd(),
129130
cmRoot: '',
@@ -154,11 +155,15 @@ export function createWorkflowMachine(initialContext: Partial<WorkflowContext> =
154155
on: {
155156
STEP_COMPLETE: [
156157
// Controller mode -> delegated state
158+
// For interactive steps, requires a controller to be configured
159+
// For non-interactive steps (interactive: false), controller is not needed (scenarios 5-6)
157160
{
158161
target: 'delegated',
159162
guard: (ctx) => {
160163
const step = ctx.steps[ctx.currentStepIndex];
161-
return ctx.autoMode && !ctx.paused && step?.interactive !== false;
164+
const isInteractive = step?.interactive !== false;
165+
// Interactive steps need a controller; non-interactive steps can be fully autonomous
166+
return ctx.autoMode && !ctx.paused && (ctx.hasController || !isInteractive);
162167
},
163168
action: (ctx, event) => {
164169
if (event.type === 'STEP_COMPLETE') {

src/workflows/state/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ export interface WorkflowContext {
6161
// Input mode
6262
autoMode: boolean;
6363

64+
// Controller availability (for FSM guard)
65+
hasController: boolean;
66+
6467
// Pause state
6568
paused: boolean;
6669

0 commit comments

Comments
 (0)