Skip to content

feat(workflow-executor): ai-assisted action forms, ai pre-fills then human submits (PRD-511)#1693

Open
Scra3 wants to merge 3 commits into
feature/prd-509-executor-action-form-portfrom
feature/prd-511-executor-ai-assisted-forms
Open

feat(workflow-executor): ai-assisted action forms, ai pre-fills then human submits (PRD-511)#1693
Scra3 wants to merge 3 commits into
feature/prd-509-executor-action-form-portfrom
feature/prd-511-executor-ai-assisted-forms

Conversation

@Scra3

@Scra3 Scra3 commented Jun 21, 2026

Copy link
Copy Markdown
Member

What

AI-assisted mode of the AI-assisted & Full AI action forms epic (PRD-57), built on PRD-509. "AI-assisted" = the existing AutomatedWithConfirmation mode (no new enum). The executor pre-fills the form server-side then pauses; the front executes natively.

Changes

  • Shared AI fill loop (fillFormWithAi) — bounded by max iterations (3) + no-progress detection; re-applies values each pass so dynamic-form change hooks reveal dependent fields; strict "leave empty if unsure" prompt; returns values in fill order so the front replays sequentially. Reused by Full AI (PRD-512).
  • Mode branching: formless unchanged; form + Manual → pause, NO AI prefill; form + FullyAutomated → still UnsupportedActionFormError (PRD-512 implements); form + AutomatedWithConfirmation → fill then pause.
  • Schema: TriggerAction.executionType accepts Manual and drops .catch (which silently coerced manual → AI-assisted). Regression-tested via the mapper. server-types also accepts Manual.
  • Pending payload: pendingData.form = { fields, aiFilledValues: ordered {field,value}[] }.
  • Confirmation payload: submittedValues + submissionOutcome (executed | pending-approval). Branch A accepts a pending-approval confirmation with no actionResult and persists outcome + submitted values + AI prefill (audit / downstream-AI context).

Tests

1099 tests pass (48 suites). New: Manual no-prefill, AI-assisted ordered fill (leaves no-context field empty), pending-approval persistence, manual-not-coerced.

Stacked on feature/prd-509-executor-action-form-port.

fixes PRD-511

Note

Add AI-assisted form pre-fill with human review to trigger-action workflow steps

  • Adds a Manual execution type to trigger-action steps, which pauses the workflow and presents the form to the user without AI involvement.
  • Adds an AutomatedWithConfirmation path for form-bearing actions: the executor runs an iterative AI loop (fillFormWithAi) to pre-fill form fields using workflow context, then pauses for human review before submission.
  • FullyAutomated on form-bearing actions is explicitly rejected with UnsupportedActionFormError.
  • Extends TriggerActionPendingData and execution result types to carry fields, aiFilledValues, submittedValues, and submissionOutcome (executed | pending-approval).
  • The PATCH endpoint now accepts submittedValues and submissionOutcome from the frontend via an extended triggerActionPatchSchema.
  • Risk: removing the .catch(AutomatedWithConfirmation) coercion means any existing step definition with an unrecognized executionType will no longer silently fall back.
📊 Macroscope summarized 8a4cc02. 1 file reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

…human submits (PRD-511)

When a Trigger Action step's action has a form, AI-assisted mode
(AutomatedWithConfirmation) pre-fills the form from the workflow context, then
pauses for the user to review/edit/submit natively. The executor does NOT
execute in this mode — the front does.

- shared AI fill loop (fillFormWithAi): bounded by max iterations + no-progress
  detection, re-applies values each pass so dynamic-form change hooks reveal
  dependent fields; strict "leave empty if unsure" prompt; returns values in
  fill order so the front replays sequentially. Reused by Full AI (PRD-512).
- mode branching: formless unchanged; form+Manual pauses with NO prefill;
  form+FullyAutomated still throws UnsupportedActionFormError (PRD-512);
  form+AutomatedWithConfirmation fills then pauses.
- schema: TriggerAction executionType accepts Manual + drops the .catch that
  silently coerced manual into AI-assisted (anti-coercion, regression-tested).
- pending payload carries the form + ordered AI prefill; confirmation payload
  gains submittedValues + submissionOutcome (executed or pending-approval); a
  pending-approval submission is persisted distinctly (no actionResult).

fixes PRD-511

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jun 21, 2026

Copy link
Copy Markdown

PRD-511

@qltysh

qltysh Bot commented Jun 21, 2026

Copy link
Copy Markdown

1 new issue

Tool Category Rule Count
qlty Structure Function with high complexity (count = 15): fillFormWithAi 1

@qltysh

qltysh Bot commented Jun 21, 2026

Copy link
Copy Markdown

Qlty


Coverage Impact

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

Modified Files with Diff Coverage (2)

RatingFile% DiffUncovered Line #s
New Coverage rating: A
...-executor/src/executors/trigger-record-action-step-executor.ts98.2%252
New Coverage rating: A
packages/workflow-executor/src/executors/agent-with-log.ts100.0%
Total98.2%
🤖 Increase coverage with AI coding...
In the `feature/prd-511-executor-ai-assisted-forms` branch, add test coverage for this new code:

- `packages/workflow-executor/src/executors/trigger-record-action-step-executor.ts` -- Line 252

🚦 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.

alban bertolini and others added 2 commits June 21, 2026 16:31
…thoritative (PRD-511)

The fill system prompt framed the workflow context (record data) as the only
source and forbade "inventing amounts", which conflicted with an explicit
instruction like "set the price to 35" — that value comes from the request, not
the record. The AI oscillated between the requested value and echoing the field's
current default. Clarified that an explicit value stated in the request must be
applied (not guessing), and that "current" is just a default to override.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds Debug-level traces around the AI form-fill so support can diagnose a client's
under-/mis-filled form: the context handed to the AI (request, fields, full
message list), the raw values the AI returned, and the net values retained after
the drop-stale pass (+ canExecute). Off by default — turned on per client with
LOG_LEVEL=Debug. Client-side logs only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
for (const [field, value] of Object.entries(aiValues)) {
const isEmpty = value === undefined || value === null || value === '';
const exists = form.fields.some(f => f.name === field);
const isNew = accumulator[field] !== value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Low executors/trigger-record-action-step-executor.ts:202

The isNew check on line 202 uses !== (reference comparison), so when the AI returns a structurally identical object or array value across iterations (same content, different reference), it is treated as "new." This pushes duplicate entries into ordered, inflating the aiFilledValues audit trail and triggering unnecessary sequential replays on the frontend. Consider using a value-based comparison such as JSON.stringify.

Suggested change
const isNew = accumulator[field] !== value;
const isNew = JSON.stringify(accumulator[field]) !== JSON.stringify(value);
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @packages/workflow-executor/src/executors/trigger-record-action-step-executor.ts around line 202:

The `isNew` check on line 202 uses `!==` (reference comparison), so when the AI returns a structurally identical object or array value across iterations (same content, different reference), it is treated as "new." This pushes duplicate entries into `ordered`, inflating the `aiFilledValues` audit trail and triggering unnecessary sequential replays on the frontend. Consider using a value-based comparison such as `JSON.stringify`.

Evidence trail:
packages/workflow-executor/src/executors/trigger-record-action-step-executor.ts lines 190-213 (loop with `accumulator[field] !== value` on line 202); lines 244-305 (`askAiToFillForm` returns `Record<string, unknown>`, schema uses `z.unknown()` at line 267); packages/workflow-executor/src/ports/agent-port.ts lines 61-68 (ActionFormField type is `string`, value is `unknown`); packages/workflow-executor/src/adapters/agent-client-agent-port.ts lines 300-311 (field types include 'Enum' and others via `field.getType()`); packages/workflow-executor/src/types/step-execution-data.ts lines 84-89 (AiFilledFormValue interface with `value: unknown`).

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