refactor(workflow-executor): extract ActionRef/FieldRef, align Ref shapes#1502
refactor(workflow-executor): extract ActionRef/FieldRef, align Ref shapes#1502Scra3 wants to merge 31 commits intofeat/prd-214-setup-workflow-executor-packagefrom
Conversation
…on flow Implements PRD-219. Adds an executor for update-record steps with three execution branches: automatic completion, awaiting user confirmation, and re-entry after confirmation. Extracts shared record selection helpers from ReadRecordStepExecutor into BaseStepExecutor for reuse. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cordStepExecutor Match on type + stepIndex only, then guard on toolConfirmationInterruption presence separately. Removes redundant filter predicate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…endingUpdate The field stores the proposed field update (from AI or user-edited) pending confirmation. "pendingUpdate" better describes the content than "toolConfirmationInterruption" which described the mechanism. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…f error outcome Missing pendingUpdate in Branch A is a bug (orchestrator/RunStore), not a business case — throw WorkflowExecutorError instead of returning a graceful error outcome. Also rename local variable interruption → execution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… casting
Add { skipped: true } to the executionResult union type, removing the
unsafe `as unknown as` cast when the user rejects the update.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… traceability Keep pendingUpdate in the saved execution data after both confirm and reject — useful for audit trail (what was proposed vs what happened). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ecutor
Pass userInput as parameter to handleConfirmation() where it is already
narrowed, removing the `as { confirmed: boolean }` cast. Also move
resolveFieldName inside try-catch blocks so WorkflowExecutorError is
properly caught and returned as a status: 'error' outcome.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…prove coverage - Extract resolveAndUpdate to deduplicate resolve+update+persist logic - Extract buildOutcomeResult/buildSuccessResult/buildErrorResult helpers - Move getCollectionSchema inside try-catch in confirmation flow - Rename executionParams.fieldName to fieldDisplayName for consistency - Add tests for resolveFieldName failure in both branches - Add test for relationship field exclusion from update tool schema Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dErrorResult wrappers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ment executionResult Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…StepExecutionData Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…field resolution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move findField to BaseStepExecutor with displayName priority over fieldName - Move buildOutcomeResult to BaseStepExecutor, use error !== undefined check - Fix resolveAndUpdate try-catch scope (saveStepExecution outside try block) - Fix ConditionStepExecutor catch-all to only catch WorkflowExecutorError - Add pendingUpdate visibility in step summary - Remove findField from types/record.ts and barrel export Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Condition steps handle BPMN routing, record-task steps operate on client data. The previous "ai-task" name was misleading since both step types use AI. Rename to "record-task" to reflect the actual distinction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Definition Drop recordSourceStepId and remoteToolsSourceId — both were declared but never read. allowedTools is kept as the orchestrator-provided tool filter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces StepType.ToolTask ('tool-task') and ToolTaskStepDefinition for
steps where the AI freely selects and executes a tool from allowedTools.
Moves allowedTools out of RecordTaskStepDefinition where it didn't belong.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…AndUpdate arity
- Extract UpdateTarget interface {selectedRecordRef, fieldDisplayName, value} so
resolveAndUpdate takes 2 params instead of 4 (addresses qlty reviewer comment)
- Collapse 3 uninitialized let declarations in handleFirstCall into one target object
- Fix missing runId in getAvailableRecordRefs and update-record saveStepExecution calls
- Strengthen test assertions to include runId as first arg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ecutor Code changes: - Add JSDoc on buildOutcomeResult warning it is for record-task executors only - Add runtime guard on userInput.type in handleConfirmation (WorkflowExecutorError if an unexpected type is received, guarding against future UserInput union extension) New tests: - buildStepSummary: pendingUpdate branch coverage (update-record step with pendingUpdate but no executionParams emits Pending: in AI context) - handleConfirmation: stepIndex mismatch and missing pendingUpdate cases - stepOutcome shape: type/stepId/stepIndex asserted on happy path - unexpected userInput type: runtime guard verified - findField fieldName fallback: update succeeds when AI returns raw fieldName - schema caching: getCollectionSchema called once per collection in Branch B - RunStore error propagation: all four saveStepExecution/getStepExecutions call sites Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pletion→automaticExecution - StepType.ToolTask → StepType.McpTask (wire value: 'tool-task' → 'mcp-task') - ToolTaskStepDefinition → McpTaskStepDefinition - mcpServerId: string[] → string (single ID) - automaticCompletion → automaticExecution on both RecordTaskStepDefinition and McpTaskStepDefinition - Remove TODO rename comments - Update all tests accordingly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…record-1' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the UserInput discriminated union with a plain boolean field userConfirmed on ExecutionContext and PendingStepExecution. Since the only user input type in practice is a boolean confirmation, the union wrapper adds complexity with no benefit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion flow
Implements TriggerActionStepExecutor following the UpdateRecordStepExecutor
pattern (branches A/B/C, confirmation flow, automaticExecution).
- Add TriggerActionStepExecutionData type with executionParams (actionDisplayName
+ actionName), executionResult ({ success } | { skipped }), and pendingAction
- Add NoActionsError for collections with no actions
- Implement selectAction via AI tool with displayName enum and technical name hints
- resolveAndExecute stores the technical actionName in executionParams for
traceability; action result discarded per privacy constraint
- Fix buildStepSummary in BaseStepExecutor to include trigger-action pendingAction
in prior-step AI context (parity with update-record pendingUpdate)
- Export TriggerActionStepExecutor, TriggerActionStepExecutionData, NoActionsError
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t and pendingAction Resolve actionName once in handleFirstCall and store it in pendingAction, so resolveAndExecute receives it directly via ActionTarget without re-fetching the schema. Rename TriggerTarget → ActionTarget for consistency with English naming conventions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…f shapes to { name, displayName }
- Extract ActionRef { name, displayName } from inline types in TriggerActionStepExecutionData
- Extract FieldRef { name, displayName } replacing FieldReadBase in ReadRecord types
- UpdateRecordStepExecutionData executionParams/pendingUpdate now use FieldRef & { value }
- Rename actionDisplayName/actionName → displayName/name, fieldDisplayName/fieldName → displayName/name
- Move resolveFieldName to handleFirstCall (no re-resolution in resolveAndUpdate)
- Add missing tests: resolveActionName not-found path, saveStepExecution not-called assertions, trigger-action pendingAction in buildStepSummary
- Export ActionRef, FieldRef, NoActionsError from index; update CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| export type { | ||
| ConditionStepDefinition, | ||
| AiTaskStepDefinition, | ||
| RecordTaskStepDefinition, | ||
| StepDefinition, | ||
| } from './types/step-definition'; |
There was a problem hiding this comment.
🟡 Medium src/index.ts:2
McpTaskStepDefinition is defined and included in the StepDefinition union, but not exported from index.ts. Consumers cannot import this type directly to narrow or type-guard MCP task steps. Consider adding it to the named type exports.
export type {
ConditionStepDefinition,
RecordTaskStepDefinition,
StepDefinition,
} from './types/step-definition';🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file packages/workflow-executor/src/index.ts around lines 2-6:
`McpTaskStepDefinition` is defined and included in the `StepDefinition` union, but not exported from `index.ts`. Consumers cannot import this type directly to narrow or type-guard MCP task steps. Consider adding it to the named type exports.
Evidence trail:
packages/workflow-executor/src/index.ts (lines 2-6): exports `ConditionStepDefinition`, `RecordTaskStepDefinition`, `StepDefinition` but not `McpTaskStepDefinition`
packages/workflow-executor/src/types/step-definition.ts (lines 30-35): defines `McpTaskStepDefinition`
packages/workflow-executor/src/types/step-definition.ts (lines 37-40): `StepDefinition` union includes `McpTaskStepDefinition`
|
Coverage Impact Unable to calculate total coverage change because base branch coverage was not found. Modified Files with Diff Coverage (9) 🛟 Help
|
…store display names in executionParams Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rams for consistency Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-executor Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…y" wording Use fieldNames instead of fieldDisplayNames and actionName instead of displayName in tool schemas exposed to the AI, to avoid confusion between property names and their semantics (values remain display names). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ize pending state - Make buildOutcomeResult abstract on BaseStepExecutor; each branch owns its outcome shape - Add RecordTaskStepExecutor intermediate class implementing buildOutcomeResult for 'record-task' - Add pendingData to BaseStepExecutionData, replacing pendingUpdate/pendingAction per-type fields; each executor sets it when saving, base class reads it directly with no type discrimination - Rename TriggerActionStepExecutor → TriggerRecordActionStepExecutor for naming consistency with ReadRecord/UpdateRecord Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…idate error handling - Introduce doExecute() as the abstract hook; execute() in the base class wraps it with the single try-catch that converts WorkflowExecutorErrors to error outcomes and rethrows infrastructure errors — removing all try-catch boilerplate from subclasses - Inline the former buildErrorOutcomeOrThrow helper directly into execute(), it no longer needs to be a named method - Add StepPersistenceError (extends WorkflowExecutorError) for post-side-effect persistence failures; these are now consistently converted to error outcomes - Extract handleConfirmationFlow into RecordTaskStepExecutor to DRY the confirmation pattern shared by update-record and trigger-record-action - Split the !execution?.pendingData guard into two distinct WorkflowExecutorErrors for clearer debug messages - Export BaseStepStatus from step-outcome; remove pendingData from BaseStepExecutionData (declared only on the concrete types that need it) - Fix extractToolCallArgs to throw MalformedToolCallError when args is null/undefined Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Summary
ActionRefandFieldRefshared interfaces ({ name, displayName }) from inline types inTriggerActionStepExecutionDataandUpdateRecordStepExecutionDataFieldReadBase(private) with exportedFieldRef—FieldReadSuccess/FieldReadErrornow extendFieldRefactionDisplayName/actionName→displayName/nameandfieldDisplayName/fieldName→displayName/namefor consistencyresolveFieldNametohandleFirstCallso the technical field name is stored inpendingUpdateat creation time (no re-resolution inresolveAndUpdate)resolveActionNamenot-found path,saveStepExecutionnot-called assertions onWorkflowExecutorError,trigger-actionpending action inbuildStepSummaryActionRef,FieldRef,NoActionsErrorfrom index; updateCLAUDE.mdTest plan
yarn workspace @forestadmin/workflow-executor test— 144 tests passyarn workspace @forestadmin/workflow-executor build— no errorsyarn workspace @forestadmin/workflow-executor lint— clean🤖 Generated with Claude Code
Note
Extract
ActionRef/FieldReftypes and addUpdateRecordStepExecutor/TriggerRecordActionStepExecutorRecordTaskStepDefinitionandRecordTaskStepExecutionData(replacingAiTask*equivalents), and addsFieldRef/ActionRefas shared named-field reference shapes across execution data types.UpdateRecordStepExecutor(selects a field and value, supports confirmation flow) andTriggerRecordActionStepExecutor(selects and triggers a collection action, supports confirmation flow).RecordTaskStepExecutorbase class;BaseStepExecutorgains schema caching,selectRecordRef,findField, andgetAvailableRecordRefshelpers.BaseStepExecutor.executenow catchesWorkflowExecutorErrorand returns a structured error outcome instead of throwing;context.historyis renamed tocontext.previousSteps.AiTaskStepOutcome,AiTaskStepStatus,AiTaskStepExecutionData, andUserInputare removed from the public API; callers must migrate toRecordTask*equivalents.Macroscope summarized c8b9247.