feat(workflow-executor): add 3-way execution mode to the load related record step (PRD-148)#1728
Conversation
… record step (PRD-148) Manual presents the narrowed list with no AI; AI-assisted suggests a record; Full AI selects + loads and auto-skips on no source record, empty list, or AI judging none relevant. selectedRecordRef is optional now (no-source skip); the wire serializer guards it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3 new issues
|
|
Coverage Impact ⬆️ Merging this pull request will increase total coverage on Modified Files with Diff Coverage (2)
🛟 Help
|
…resolveTarget (PRD-148) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… candidate list (PRD-148) When the HasMany candidate list is truncated to fit the AI prompt budget, -1 (none relevant) is no longer offered — the AI picks from what it saw, so a match in the unseen tail is not silently skipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…PRD-148) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e AI decline (PRD-148) Full AI no longer silently skips when it can't load a relevant record. With nothing relevant it degrades to an AI-assisted confirmation (a human decides, "No X to load" pre-checked); no source record now surfaces an error like the await modes instead of skipping. AI-assisted may decline too (allowNone), and the record-selection prompt tells the AI to return -1 on an impossible request rather than forcing a weak match. pendingData carries a suggestNoRecord flag so the front distinguishes an active "nothing relevant" from a plain no-suggestion (Manual). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin LoadRelatedRecordStepDefinitionSchema behaviour: each valid mode parses to its own value (incl. 'manual' → 'manual'), a missing mode defaults to AutomatedWithConfirmation, and an invalid mode is rejected rather than silently coerced (no `.catch`, so a `manual` typo can't flip AI prefill back on). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…AI failure Load Related Record 3-way execution mode hardening. Bundled because the changes are interwoven in the executor and its test: - feat: Full AI now degrades to an AI-assisted confirmation when the AI returns a best guess but cannot confidently single one out among several viable candidates. selectBestRecordIndex gains a `confident` flag (distinct from the existing -1 "none relevant" sentinel, whose semantics are unchanged); an ambiguous positive pick pre-selects the best guess as suggestedRecord for a human to confirm instead of auto-loading. Confident picks still auto-load; a missing flag defaults to confident (backward compatible). - fix: AI failure/timeout in any AI mode (relation pick, field/record ranking, or a Full AI auto-load) now degrades to the deterministic Manual path — deterministic candidate list presented for the user to pick — instead of erroring the run or auto-skipping. AI calls are tagged via an internal marker so genuine config/data errors still surface. - fix: refreshCandidatesForField rebuilds pendingData from scratch on a field switch (keeping only the immutable relation list) so no stale suggestion state (suggestedRecord / suggestNoRecord) lingers. - style: drop ticket IDs from comments and test titles. - test: pin the truncated-list none-guard (noneAllowed = allowNone && !truncated — the AI is not offered -1 when the list was truncated). - tighten selectBestFromRelatedData options so allowNone only applies when rank is true. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Close the changed-lines coverage gap on the refreshCandidatesForField AI-failure catch: add a test where the ranking AI call throws during a field switch and the step degrades to the deterministic no-AI list (false branch), and one where a non-AI data-fetch failure is rethrown as a step error rather than degrading (true branch). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t name Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…able Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scra3
left a comment
There was a problem hiding this comment.
Review of the 3-way execution mode against the PRD-148 acceptance criteria (plus a skeptic second pass on the findings). The edge-case handling is solid overall — Manual never calls the AI, no-source throws instead of auto-advancing, AI failure degrades to Manual, and -1/allowNone is correctly gated on a non-truncated list. Two points below: one blocking edge case in Full AI, one non-blocking error-classification smell.
Scra3
left a comment
There was a problem hiding this comment.
Follow-up architecture notes (non-blocking, complements the earlier review). Overall the executor conforms well to the trigger-record-action precedent — base machinery (invokeWithTool, handleConfirmationFlow, patchAndReloadPendingData) is reused rather than re-implemented, and the 3-mode schema mirrors TriggerAction. One inline placement suggestion, plus a nit: the sharedFields header comment (src/types/validated/step-definition.ts:23-24) still claims every schema normalizes via .default().catch(), now false for the LoadRelatedRecord and TriggerAction schemas that intentionally drop .catch — update it to avoid misleading future edits.
A HasMany/BelongsToMany relation filtered to one candidate was auto-loaded as confident without consulting the AI, so Full AI could load an irrelevant sole record instead of degrading to 'No X to load'. It now goes through AI ranking (allowNone) so the AI can decline (-1). withAiAssist in selectRelationToFollow now wraps only the AI call, not the run-store read, so a store failure surfaces instead of being mistagged and degraded to Manual. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Matches house style (exported from errors.ts, kept out of the index barrel like AiInvokeTimeoutError so it stays internal and never HTTP-maps) and drops the file-level max-classes-per-file suppression. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The lone-toMany ranking fix also dropped Manual's single-candidate pre-selection (bestIndex -1), regressing prefill and diverging from xToOne Manual. The !opts.rank branch now returns bestIndex 0 only when there is exactly one candidate; with several the user still picks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

What
Execution logic for the 3-way Execution mode (Manual / AI-assisted / Full AI) on the workflow Load Related Record step. Part of PRD-148 — 3-PR set (executor +
ForestAdmin/forestadmin-serverorchestrator +ForestAdmin/forestadmineditor).manual): present the narrowed candidate list with no AI — no relation chooser, no record suggestion. Single-record relations (xToOne, or HasMany with 1 candidate) still pre-fill, since that's the only option, not an AI pick. The Zod schema drops.catch(AutomatedWithConfirmation)and addsManual, so amanualvalue is no longer silently coerced back into AI prefill (same reasoning as TriggerAction).automated-with-confirmation, default): AI suggests a record, user confirms (unchanged).fully-automated): AI selects and loads. Auto-skips (persistsexecutionResult: { skipped: true }+success) when there is no source record (SourceRecordMissingError, Full-AI only), an empty candidate list, or the AI judges none relevant (select-record-by-contentreturns-1via the new Full-AI-onlyallowNone).selectedRecordRefis now optional onLoadRelatedRecordStepExecutionData(absent only on the no-source Full-AI skip); the wire serializer omits it instead of dereferencingundefined.Tests
97 tests in the executor suite. Added: Manual = no AI / no pre-selection (incl. relation switch), Manual single-record pre-fill, Full-AI skip on each of the 3 causes, await-mode no-source still errors (regression guard), serializer no-source round-trip.
Validation
Validated end-to-end on the local stack (account → bills) across the 3 modes, plus a local multi-agent code review — 1 Critical found & fixed: serializer crash (503) on the no-source Full-AI skip.
Relates to PRD-148.
🤖 Generated with Claude Code
Note
Add Manual execution mode to
LoadRelatedRecordStepExecutorwith auto-skip for FullyAutomatedManualas a validexecutionTypefor the load-related-record step; in Manual mode, AI is not called for relation selection or candidate suggestion — the first eligible relation is picked deterministically and nosuggestedRecordis included.FullyAutomatedmode, steps now auto-skip (via newpersistSkip) when there is no source record, no linked xToOne record, or when the AI ranks all candidates as irrelevant (returns-1).selectBestRecordIndexgains anallowNoneoption so AI can return-1only when the candidate list was not truncated, preventing forced selections.fetchRecordForRelationandfetchXToOneRecordRefnow returnnullon absence instead of throwing, with callers deciding skip vs. error behavior.selectedRecordRefis made optional inLoadRelatedRecordStepExecutionDataand the wire serializer skips the field when absent, fixing a serialization crash on auto-skipped steps.executionTypeschema no longer coerces unknown values toAutomatedWithConfirmation; invalid values will now fail validation instead of being silently swallowed.Changes since #1728 opened
suggestNoRecordflag instead of auto-skipping when no relevant record exists [9ee1164]suggestNoRecordfield toLoadRelatedRecordPendingDataand updatedLoadRelatedRecordStepExecutionDatainterface documentation [9ee1164]collectCandidateIdsandresolveAndLoadAutomatic[9ee1164]LoadRelatedRecordStepExecutordegrades to manual candidate selection when AI ranking fails during field switch [71542cd]LoadRelatedRecordStepExecutorpropagates non-AI errors during field switch instead of degrading [71542cd]LoadRelatedRecordStepExecutor.resolveAndLoadAutomaticmethod [44e23be]LoadRelatedRecordStepExecutorFullyAutomated BelongsTo functionality [44e23be]AiAssistUnavailableErrorclass and multiple methods inLoadRelatedRecordStepExecutorincludingdoExecute,refreshCandidatesForField,degradeToManualAwaitInput,selectBestFromRelatedData,withAiAssist, andselectBestRecordIndexwithin theworkflow-executorpackage [9fcdda2]load-related-record-step-executor.test.tsfile in theworkflow-executorpackage [9fcdda2]workflow-executorpackage'sLoadRelatedRecordStepExecutorto consult AI for single-candidate scenarios during related record ranking [04b2be8]workflow-executorpackage'sLoadRelatedRecordStepExecutormethods for relation selection and candidate refresh [04b2be8]workflow-executorpackage'sload-related-record-step-executor.test.tsto reflect AI consultation for single-candidate scenarios and narrowed AI-wrapping scope [04b2be8]AiAssistUnavailableErrorclass fromload-related-record-step-executorinto a shared error class in theworkflow-executorpackage [53670e4]LoadRelatedRecordStepExecutorclass inworkflow-executorpackage to pre-select a single related record candidate in manual execution mode [2f50ee3]Macroscope summarized 4f8b398.