Skip to content

feat(workflow-executor): add 3-way execution mode to the load related record step (PRD-148)#1728

Open
matthv wants to merge 11 commits into
mainfrom
feature/prd-148-ai-assisted-and-full-ai-load-related-record-step
Open

feat(workflow-executor): add 3-way execution mode to the load related record step (PRD-148)#1728
matthv wants to merge 11 commits into
mainfrom
feature/prd-148-ai-assisted-and-full-ai-load-related-record-step

Conversation

@matthv

@matthv matthv commented Jun 30, 2026

Copy link
Copy Markdown
Member

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-server orchestrator + ForestAdmin/forestadmin editor).

  • Manual (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 adds Manual, so a manual value is no longer silently coerced back into AI prefill (same reasoning as TriggerAction).
  • AI-assisted (automated-with-confirmation, default): AI suggests a record, user confirms (unchanged).
  • Full AI (fully-automated): AI selects and loads. Auto-skips (persists executionResult: { 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-content returns -1 via the new Full-AI-only allowNone).

selectedRecordRef is now optional on LoadRelatedRecordStepExecutionData (absent only on the no-source Full-AI skip); the wire serializer omits it instead of dereferencing undefined.

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 LoadRelatedRecordStepExecutor with auto-skip for FullyAutomated

  • Adds Manual as a valid executionType for 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 no suggestedRecord is included.
  • In FullyAutomated mode, steps now auto-skip (via new persistSkip) when there is no source record, no linked xToOne record, or when the AI ranks all candidates as irrelevant (returns -1).
  • selectBestRecordIndex gains an allowNone option so AI can return -1 only when the candidate list was not truncated, preventing forced selections.
  • fetchRecordForRelation and fetchXToOneRecordRef now return null on absence instead of throwing, with callers deciding skip vs. error behavior.
  • selectedRecordRef is made optional in LoadRelatedRecordStepExecutionData and the wire serializer skips the field when absent, fixing a serialization crash on auto-skipped steps.
  • Behavioral Change: executionType schema no longer coerces unknown values to AutomatedWithConfirmation; invalid values will now fail validation instead of being silently swallowed.

Changes since #1728 opened

  • Added 3-way execution mode allowing AI to decline record selection by returning -1 when no candidate satisfies the request [9ee1164]
  • Changed FullyAutomated execution mode to fall back to awaiting-input with suggestNoRecord flag instead of auto-skipping when no relevant record exists [9ee1164]
  • Added suggestNoRecord field to LoadRelatedRecordPendingData and updated LoadRelatedRecordStepExecutionData interface documentation [9ee1164]
  • Removed helper methods and unified record resolution logic through collectCandidateIds and resolveAndLoadAutomatic [9ee1164]
  • Updated test suite to validate new awaiting-input fallback behavior and AI -1 guidance [9ee1164]
  • Implemented AI failure degradation logic throughout LoadRelatedRecordStepExecutor to convert AI unavailability or errors into deterministic manual fallback execution [f90b868]
  • Implemented confidence-based record selection for fully automated mode where low-confidence AI picks trigger user confirmation instead of auto-loading [f90b868]
  • Modified LoadRelatedRecordStepExecutor.refreshCandidatesForField to rebuild pendingData from scratch when switching target relations and conditionally set suggestNoRecord only when AI was attempted and returned no suggestion [f90b868]
  • Updated select-record-by-content AI tool schema to accept and return a confident boolean parameter indicating AI confidence in record selection [f90b868]
  • Updated and expanded tests in load-related-record-step-executor.test.ts to verify AI degradation behavior, low-confidence handling, and stale state clearing [f90b868]
  • Created step-definition.test.ts to validate LoadRelatedRecordStepDefinitionSchema parsing of executionType enum values [f90b868]
  • Added test verifying LoadRelatedRecordStepExecutor degrades to manual candidate selection when AI ranking fails during field switch [71542cd]
  • Added test verifying LoadRelatedRecordStepExecutor propagates non-AI errors during field switch instead of degrading [71542cd]
  • Updated inline comment in LoadRelatedRecordStepExecutor.resolveAndLoadAutomatic method [44e23be]
  • Renamed test case for LoadRelatedRecordStepExecutor FullyAutomated BelongsTo functionality [44e23be]
  • Appended explanatory comment to ESLint directive [08c1949]
  • Revised documentation comments across AiAssistUnavailableError class and multiple methods in LoadRelatedRecordStepExecutor including doExecute, refreshCandidatesForField, degradeToManualAwaitInput, selectBestFromRelatedData, withAiAssist, and selectBestRecordIndex within the workflow-executor package [9fcdda2]
  • Updated inline test comments throughout the load-related-record-step-executor.test.ts file in the workflow-executor package [9fcdda2]

Macroscope summarized 4f8b398.

… 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>
@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

PRD-148

@qltysh

qltysh Bot commented Jun 30, 2026

Copy link
Copy Markdown

3 new issues

Tool Category Rule Count
qlty Structure Function with many parameters (count = 4): selectBestFromRelatedData 2
qlty Structure Function with high complexity (count = 14): selectBestRecordIndex 1

@qltysh

qltysh Bot commented Jun 30, 2026

Copy link
Copy Markdown

Qlty


Coverage Impact

⬆️ Merging this pull request will increase total coverage on main by 0.12%.

Modified Files with Diff Coverage (1)

RatingFile% DiffUncovered Line #s
Coverage rating: A Coverage rating: A
...ow-executor/src/executors/load-related-record-step-executor.ts100.0%
Total100.0%
🚦 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.

…resolveTarget (PRD-148)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
matthv and others added 2 commits June 30, 2026 15:13
… 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>
Comment thread packages/workflow-executor/src/executors/load-related-record-step-executor.ts Outdated
Comment thread packages/workflow-executor/src/types/validated/step-definition.ts
Comment thread packages/workflow-executor/src/executors/load-related-record-step-executor.ts Outdated
Comment thread packages/workflow-executor/src/types/step-execution-data.ts
Comment thread packages/workflow-executor/src/types/validated/step-definition.ts
…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>
hercemer42 and others added 6 commits July 2, 2026 15:33
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>
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.

2 participants