feat(workflow-executor): add UpdateRecordStepExecutor with confirmation flow#1499
Conversation
packages/workflow-executor/src/executors/update-record-step-executor.ts
Outdated
Show resolved
Hide resolved
|
Coverage Impact Unable to calculate total coverage change because base branch coverage was not found. Modified Files with Diff Coverage (7) 🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
2dc8c24 to
bb2901c
Compare
8d13ac5 to
f8323f8
Compare
…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>
b6dc9a5 to
6a49657
Compare
…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>
| export interface AiTaskStepOutcome extends BaseStepOutcome { | ||
| type: 'ai-task'; | ||
| status: AiTaskStepStatus; | ||
| export interface RecordTaskStepOutcome extends BaseStepOutcome { |
There was a problem hiding this comment.
Ai task was too ambiguous, record task is related to the forest record
| automaticCompletion?: boolean; | ||
| } | ||
|
|
||
| export interface ToolTaskStepDefinition extends BaseStepDefinition { |
There was a problem hiding this comment.
A task that wants to execute a tool
…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>
| @@ -1,14 +1,14 @@ | |||
| export { StepType } from './types/step-definition'; | |||
| export type { | |||
There was a problem hiding this comment.
🟡 Medium src/index.ts:2
McpTaskStepDefinition is part of the StepDefinition union but is not exported from index.ts, while ConditionStepDefinition and RecordTaskStepDefinition are. Consumers cannot import the type directly to work with MCP task step definitions specifically.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file packages/workflow-executor/src/index.ts around line 2:
`McpTaskStepDefinition` is part of the `StepDefinition` union but is not exported from `index.ts`, while `ConditionStepDefinition` and `RecordTaskStepDefinition` are. Consumers cannot import the type directly to work with MCP task step definitions specifically.
Evidence trail:
packages/workflow-executor/src/index.ts lines 2-5 (exports `ConditionStepDefinition`, `RecordTaskStepDefinition`, `StepDefinition` but not `McpTaskStepDefinition`); packages/workflow-executor/src/types/step-definition.ts lines 29-33 (`McpTaskStepDefinition` interface definition) and lines 35-38 (`StepDefinition` union includes `McpTaskStepDefinition`)
…record-1' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ac706e8 to
1309b54
Compare
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>

Summary
UpdateRecordStepExecutorwith 3 execution branches — automatic completion (Branch B), awaiting user confirmation (Branch C), and re-entry after confirmation accept/reject (Branch A)selectRecordRef,getAvailableRecordRefs,getCollectionSchema,toRecordIdentifier,schemaCache) fromReadRecordStepExecutorintoBaseStepExecutorfor DRY reuseUpdateRecordStepExecutionDatatype,NoWritableFieldsError, anduserInputonExecutionContextTest plan
fixes PRD-219
🤖 Generated with Claude Code
Note
Add
UpdateRecordStepExecutorwith user confirmation flow to workflow-executorUpdateRecordStepExecutorthat uses an AI tool to select a writable field and value, then either updates immediately (automaticExecution: true) or returnsawaiting-inputfor user confirmation.context.userConfirmed, the executor fetches the prior execution and either applies the update or marks the step skipped.BaseStepExecutorhelpers and reused byReadRecordStepExecutor.AiTask*types toRecordTask*throughout (outcomes, execution data, step definitions) and replaceshistorywithpreviousStepsinExecutionContext.NoWritableFieldsErrorandMcpTaskstep type to the public API.ConditionStepExecutorandReadRecordStepExecutornow rethrow non-WorkflowExecutorErrorfailures instead of converting them to error outcomes.Macroscope summarized d99fe27.