feat(workflow-executor): add ReadRecordStepExecutor#1497
feat(workflow-executor): add ReadRecordStepExecutor#1497Scra3 wants to merge 33 commits intofeat/prd-214-setup-workflow-executor-packagefrom
Conversation
|
Coverage Impact Unable to calculate total coverage change because base branch coverage was not found. Modified Files with Diff Coverage (8) 🛟 Help
|
f6b101a to
71c4104
Compare
| } | ||
|
|
||
| export interface RecordData extends RecordRef { | ||
| stepIndex: number; |
There was a problem hiding this comment.
want to save the stepIndex to be sure to find the right o step in the workflow
55b265f to
25fdd06
Compare
| import type { StepExecutionData } from '../types/step-execution-data'; | ||
|
|
||
| export interface RunStore { | ||
| getRecords(): Promise<RecordData[]>; |
Implement the read-record step executor that allows AI to read fields from a record. Includes AI-based record selection when multiple records are available, and field selection via the read-selected-record-field tool. fixes PRD-218 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…epExecutor - Extract toRecordIdentifier helper to avoid duplicate template string - Add test: AI selects non-existent record identifier - Add saveStepExecution not-called assertions to no-records and no-readable-fields tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ce symmetry Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…to single FieldReadResult Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…elds across FieldRead types Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…c method Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… array schema - Remove unused FieldReadResults type alias - Rename read-selected-record-field to read-selected-record-fields - fieldNames is now always string[] (no more single string union) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move FieldReadResult types from executor to step-execution-data.ts - Add ReadRecordStepExecutionData with typed executionParams/executionResult - Use type: 'read-record' discriminator instead of generic 'ai-task' - Export new types from index.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…equired - executionParams and executionResult are now required (align with ReadRecord) - Reorganize step-execution-data.ts by section (Condition, ReadRecord, Generic) - Fix base-step-executor tests for required fields Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused fieldNames variable in buildReadFieldTool - Fix prettier formatting - Fix duplicate test title in base-step-executor Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ypes Split the old CollectionRef → RecordData hierarchy into independent schema types (CollectionSchema, FieldSchema) and data types (RecordRef, RecordData). Schema is now sourced from WorkflowPort.getCollectionSchema, records from AgentPort/RunStore. Remove dead fields (type, referencedCollectionName) and dead code (availableRecords). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ove AgentPort.getActions Actions metadata is structural info belonging to CollectionSchema, not an AgentPort concern. Remove the redundant getActions method from AgentPort and rename ActionRef to ActionSchema for naming consistency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove getRecord, saveRecord, and getStepExecution from RunStore interface — none are called in production code. Records are accessed via getRecords, step executions via getStepExecutions/saveStepExecution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ord + LoadRelatedRecordStepExecutionData Records are now accessed via ExecutionContext.baseRecord (trigger record) and LoadRelatedRecordStepExecutionData entries in the RunStore, removing the separate getRecords() method. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add isExecutedStepOnExecutor type guard and ExecutedStepExecutionData union type. buildStepSummary now outputs executionResult so subsequent steps can access field values read by earlier steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
executionParams is the input (AI decision), executionResult is the output. Rename the summary label accordingly for clarity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The field holds a RecordRef, not a RecordData. Rename for clarity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ues via AgentPort The orchestrator only sends a record reference (collection + recordId), not client data. ReadRecordStepExecutor now fetches values through agentPort.getRecord() when needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
35a89ec to
33e5552
Compare
…ta type
- Only catch WorkflowExecutorError in execute(), re-throw infrastructure errors
- Redefine RecordData as Omit<RecordRef, 'stepIndex'> & { values }
- Change LoadRelatedRecordStepExecutionData.record to RecordRef
- Use RecordData directly in AgentPort (remove AgentRecord alias)
- Update tests: model errors propagate, add RecordNotFoundError test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Context Make ExecutionContext generic with TStep/THistory defaults so executors read step and stepHistory from context instead of execute() arguments. Also rename selectRecord → selectRecordRef and getAvailableRecords → getAvailableRecordRefs for consistency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename agentRecord → recordData and readFieldValues → formatFieldResults for clarity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| fieldNames: string[], | ||
| ): FieldReadResult[] { | ||
| return fieldNames.map(name => { | ||
| const field = schema.fields.find(f => f.fieldName === name || f.displayName === name); |
There was a problem hiding this comment.
🟡 Medium executors/read-record-step-executor.ts:167
In formatFieldResults, the field lookup uses f.fieldName === name || f.displayName === name, checking fieldName before displayName. Since the AI tool in buildReadFieldTool is told to use displayNames as possible values, a field with displayName = "Status" could be shadowed by another field with fieldName = "Status", causing the wrong field's value to be returned.
- const field = schema.fields.find(f => f.fieldName === name || f.displayName === name);
+ const field = schema.fields.find(f => f.displayName === name || f.fieldName === name);🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file packages/workflow-executor/src/executors/read-record-step-executor.ts around line 167:
In `formatFieldResults`, the field lookup uses `f.fieldName === name || f.displayName === name`, checking `fieldName` before `displayName`. Since the AI tool in `buildReadFieldTool` is told to use `displayNames` as possible values, a field with `displayName = "Status"` could be shadowed by another field with `fieldName = "Status"`, causing the wrong field's value to be returned.
Evidence trail:
packages/workflow-executor/src/executors/read-record-step-executor.ts lines 134-159 (buildReadFieldTool telling AI to use displayNames) and lines 161-176 (formatFieldResults using `f.fieldName === name || f.displayName === name`); packages/workflow-executor/src/types/record.ts lines 5-9 (FieldSchema with separate fieldName and displayName).
There was a problem hiding this comment.
Not fixed — both lookup orders have the same shadowing risk in opposite directions. The current order is ISO frontend. In practice, fieldName and displayName are almost never the same string across different fields, so the collision scenario is theoretical.
…ep summary Add null check before stringifying executionParams in buildStepSummary, since AiTaskStepExecutionData.executionParams is optional. Also fix prettier issue on selectRecordRef signature. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ils import ServerUtils is now properly exported from @forestadmin/forestadmin-client. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add test for undefined executionParams in step summary and instantiate NoRecordsError to meet diff coverage threshold. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pExecution Replace separate step/stepHistory fields with currentStep: StepRecord for consistency with the existing StepRecord type. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and add stepIndex to StepDefinition Executors now construct StepHistory internally instead of receiving it via context. Also removes unused StepCategory type and simplifies selectedRecordRef spread. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ed tool func callbacks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tract id/stepIndex from StepDefinition StepHistory → StepOutcome for clarity (it's the outcome of a step, not history). Moved id/stepIndex from StepDefinition (definition concern) to ExecutionContext and PendingStepExecution (instance concern). Renamed StepRecord to Step. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Summary
ReadRecordStepExecutor— first AI-task step executor that reads fields from a recordselect-recordtool)read-selected-record-fieldtool (supports single/multiple fields, displayName + fieldName resolution)NoRecordsError,NoReadableFieldsErrorTest plan
fixes PRD-218
🤖 Generated with Claude Code
Note
Add
ReadRecordStepExecutorfor AI-driven record field selection and readingReadRecordStepExecutorthat selects a record (prompting the model when multiple are available), picks relevant fields via a tool call, reads them viaagentPort, and persists results with per-field success/error entries.StepHistory→StepOutcome,CollectionRef→CollectionSchema,RecordFieldRef→FieldSchema, and related types throughout the package to clarify semantics.getActionsfromAgentPortand trimsRunStoreto onlygetStepExecutions/saveStepExecution;WorkflowPort.getCollectionRefis renamed togetCollectionSchema.BaseStepExecutor.buildStepSummarynow emits separateInput:/Output:lines when execution data exists, replacing the previous singleResult:line.NoRecordsErrorandNoReadableFieldsErroras typedWorkflowExecutorErrorsubclasses, and exportsisExecutedStepOnExecutoras a runtime type guard.RunStore,AgentPort, andWorkflowPortinterface removals are breaking changes for any existing implementations outside this package.Macroscope summarized 650e70b.