Skip to content

feat(workflow-executor): add UpdateRecordStepExecutor with confirmation flow#1499

Open
Scra3 wants to merge 22 commits intofeat/prd-214-setup-workflow-executor-packagefrom
feat/prd-219-update-record-step-executor
Open

feat(workflow-executor): add UpdateRecordStepExecutor with confirmation flow#1499
Scra3 wants to merge 22 commits intofeat/prd-214-setup-workflow-executor-packagefrom
feat/prd-219-update-record-step-executor

Conversation

@Scra3
Copy link
Member

@Scra3 Scra3 commented Mar 19, 2026

Summary

  • Implements PRD-219: UpdateRecordStepExecutor with 3 execution branches — automatic completion (Branch B), awaiting user confirmation (Branch C), and re-entry after confirmation accept/reject (Branch A)
  • Extracts shared helpers (selectRecordRef, getAvailableRecordRefs, getCollectionSchema, toRecordIdentifier, schemaCache) from ReadRecordStepExecutor into BaseStepExecutor for DRY reuse
  • Adds UpdateRecordStepExecutionData type, NoWritableFieldsError, and userInput on ExecutionContext

Test plan

  • All 84 existing + new tests pass
  • ReadRecordStepExecutor tests still pass after refactor (no behavior change)
  • 11 new test cases cover: automatic completion, awaiting-input, confirmation accepted/rejected, missing interruption, multi-record AI selection, NoWritableFieldsError, malformed/missing tool calls, WorkflowExecutorError propagation, infra error propagation, default prompt fallback, previous steps context

fixes PRD-219

🤖 Generated with Claude Code

Note

Add UpdateRecordStepExecutor with user confirmation flow to workflow-executor

  • Adds UpdateRecordStepExecutor that uses an AI tool to select a writable field and value, then either updates immediately (automaticExecution: true) or returns awaiting-input for user confirmation.
  • On re-entry with context.userConfirmed, the executor fetches the prior execution and either applies the update or marks the step skipped.
  • Shared logic (record selection, schema caching, outcome building) is extracted into BaseStepExecutor helpers and reused by ReadRecordStepExecutor.
  • Renames AiTask* types to RecordTask* throughout (outcomes, execution data, step definitions) and replaces history with previousSteps in ExecutionContext.
  • Adds NoWritableFieldsError and McpTask step type to the public API.
  • Behavioral Change: ConditionStepExecutor and ReadRecordStepExecutor now rethrow non-WorkflowExecutorError failures instead of converting them to error outcomes.

Macroscope summarized d99fe27.

@linear
Copy link

linear bot commented Mar 19, 2026

@qltysh
Copy link

qltysh bot commented Mar 19, 2026

All good ✅

@qltysh
Copy link

qltysh bot commented Mar 19, 2026

Qlty

Coverage Impact

Unable to calculate total coverage change because base branch coverage was not found.

Modified Files with Diff Coverage (7)

RatingFile% DiffUncovered Line #s
New file Coverage rating: A
...s/workflow-executor/src/executors/read-record-step-executor.ts100.0%
New file Coverage rating: A
...ges/workflow-executor/src/executors/condition-step-executor.ts100.0%
New file Coverage rating: A
packages/workflow-executor/src/types/step-definition.ts100.0%
New file Coverage rating: A
packages/workflow-executor/src/executors/base-step-executor.ts100.0%
New file Coverage rating: A
...workflow-executor/src/executors/update-record-step-executor.ts98.2%84
New file Coverage rating: A
packages/workflow-executor/src/errors.ts100.0%
New file Coverage rating: A
packages/workflow-executor/src/index.ts100.0%
Total99.1%
🤖 Increase coverage with AI coding...

In the `feat/prd-219-update-record-step-executor` branch, add test coverage for this new code:

- `packages/workflow-executor/src/executors/update-record-step-executor.ts` -- Line 84

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

@Scra3 Scra3 force-pushed the feat/prd-219-update-record-step-executor branch from 2dc8c24 to bb2901c Compare March 19, 2026 14:38
Base automatically changed from feat/prd-218-read-record-step-executor to feat/prd-214-setup-workflow-executor-package March 19, 2026 14:45
@Scra3 Scra3 force-pushed the feat/prd-219-update-record-step-executor branch from 8d13ac5 to f8323f8 Compare March 19, 2026 14:50
alban bertolini and others added 17 commits March 20, 2026 09:30
…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>
@Scra3 Scra3 force-pushed the feat/prd-219-update-record-step-executor branch from b6dc9a5 to 6a49657 Compare March 20, 2026 08:34
…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 {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ai task was too ambiguous, record task is related to the forest record

automaticCompletion?: boolean;
}

export interface ToolTaskStepDefinition extends BaseStepDefinition {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A task that wants to execute a tool

alban bertolini and others added 2 commits March 20, 2026 09:56
…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 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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`)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary yet

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. 👍

…record-1'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Scra3 Scra3 force-pushed the feat/prd-219-update-record-step-executor branch from ac706e8 to 1309b54 Compare March 20, 2026 10:40
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant