Skip to content

Commit cf7d069

Browse files
authored
docs(workflow-executor): update contract to match implementation (#1511)
1 parent a52c00a commit cf7d069

1 file changed

Lines changed: 61 additions & 49 deletions

File tree

β€ŽWORKFLOW-EXECUTOR-CONTRACT.mdβ€Ž

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Workflow Executor β€” Contract Types
22

33
> Types exchanged between the **orchestrator (server)**, the **executor (agent-nodejs)**, and the **frontend**.
4-
> Last updated: 2026-03-24
4+
> Last updated: 2026-03-25
55
66
---
77

@@ -19,7 +19,6 @@ interface PendingStepExecution {
1919
baseRecordRef: RecordRef;
2020
stepDefinition: StepDefinition;
2121
previousSteps: Step[];
22-
userConfirmed?: boolean; // true = user confirmed a pending action on this step
2322
}
2423
```
2524

@@ -128,85 +127,90 @@ interface McpTaskStepOutcome {
128127

129128
After executing a step, the executor posts the outcome back to the server. The body is one of the `StepOutcome` shapes above.
130129

131-
> ⚠️ **NEVER contains client data** (field values, AI reasoning, etc.) β€” those stay in the `RunStore` on the client side.
130+
> **NEVER contains client data** (field values, AI reasoning, etc.) β€” those stay in the `RunStore` on the client side.
132131
133132
---
134133

135134
## 3. Pending Data
136135

137-
Steps that require user input pause with `status: "awaiting-input"`. The frontend writes `pendingData` to unblock them via a dedicated endpoint on the executor HTTP server.
136+
Steps that require user input pause with `status: "awaiting-input"`. The executor writes its AI-selected data to `pendingData` in the RunStore. The frontend can then override fields and confirm via the pending-data endpoint.
138137

139-
> **TODO** β€” The pending-data write endpoint is not yet implemented. Route, method, and per-step-type body shapes are TBD (PRD-240).
138+
**`PATCH /runs/:runId/steps/:stepIndex/pending-data`**
140139

141-
Once written, the frontend calls `POST /runs/:runId/trigger` and the executor resumes with `userConfirmed: true`.
140+
The frontend writes user overrides + confirmation to the executor HTTP server. Request bodies are validated per step type with strict Zod schemas β€” unknown fields are rejected with `400`.
141+
142+
Once written, the frontend calls `POST /runs/:runId/trigger`. On the next execution, the executor reads `pendingData` from the RunStore and checks `userConfirmed`:
143+
- `undefined` β†’ returns `awaiting-input` again (the step is not yet actionable)
144+
- `true` β†’ execute the confirmed action
145+
- `false` β†’ skip the step (mark as success)
142146

143147
### update-record β€” user picks a field + value to write
144148

145-
> **TODO** β€” Pending-data write endpoint TBD (PRD-240).
149+
The executor writes the AI's field selection to `pendingData`. The frontend can override `value` and confirm.
146150

151+
Stored in RunStore:
147152
```typescript
148153
interface UpdateRecordPendingData {
149-
name: string; // technical field name
150-
displayName: string; // label shown in the UI
151-
value: string; // value chosen by the user
154+
name: string; // technical field name (set by executor)
155+
displayName: string; // label shown in the UI (set by executor)
156+
value: string; // AI-proposed value; overridable by frontend
157+
userConfirmed?: boolean; // set by frontend via PATCH
152158
}
153159
```
154160

155-
### trigger-action β€” user confirmation only
161+
PATCH request body:
162+
```typescript
163+
{
164+
userConfirmed: boolean;
165+
value?: string; // optional override of AI-proposed value
166+
}
167+
```
156168

157-
No payload required from the frontend. The executor selects the action and writes `pendingData` itself (action name + displayName) to the RunStore. The frontend just confirms:
169+
### trigger-action & mcp-task β€” user confirmation only
158170

159-
```
160-
POST /runs/:runId/trigger
171+
The executor selects the action (or MCP tool) and writes `pendingData` to the RunStore. The frontend cannot override any executor-selected data β€” it only confirms or rejects.
172+
173+
PATCH request body (same for both types):
174+
```typescript
175+
{
176+
userConfirmed: boolean;
177+
}
161178
```
162179

163180
### load-related-record β€” user picks the relation and/or the record
164181

165-
The frontend can override **both** the relation (field) and the selected record.
166-
167-
> **Current status** β€” The frontend cannot yet override the AI selection. The executor HTTP server does not yet expose the pending-data write endpoint. Until it is implemented, the executor writes the AI's pick directly into `selectedRecordId`.
182+
The executor writes the AI's relation selection to `pendingData`. The frontend can override the relation, the selected record, or both.
168183

184+
Stored in RunStore:
169185
```typescript
170-
// Written by the executor; overwritable by the frontend via the pending-data endpoint (TBD)
171186
interface LoadRelatedRecordPendingData {
172-
name: string; // technical relation name
173-
displayName: string; // label shown in the UI
174-
relatedCollectionName: string; // collection of the related record
175-
suggestedFields?: string[]; // fields suggested for display
176-
selectedRecordId: Array<string|number>; // AI's pick; overwritten by the frontend via the pending-data endpoint
187+
name: string; // technical relation name
188+
displayName: string; // label shown in the UI
189+
suggestedFields?: string[]; // fields suggested for display (set by executor)
190+
selectedRecordId: Array<string|number>; // AI's pick; overridable by frontend
191+
userConfirmed?: boolean; // set by frontend via PATCH
177192
}
178193
```
179194

180-
The executor initially writes the AI's pick into `selectedRecordId`. The pending-data endpoint overwrites it (and optionally `name`, `displayName`, `relatedCollectionName`) when the user changes the selection.
181-
182-
#### Future endpoint β€” pending-data write (not yet implemented)
183-
184-
> **TODO** β€” Route and method TBD (PRD-240).
185-
186-
Request body:
195+
> `relatedCollectionName` is **not** stored in `pendingData` β€” the executor re-derives it from the `FieldSchema` at execution time using the (possibly overridden) relation `name`.
187196
197+
PATCH request body:
188198
```typescript
189199
{
190-
selectedRecordId?: Array<string | number>; // record chosen by the user
191-
name?: string; // relation changed
192-
displayName?: string; // relation changed
193-
relatedCollectionName?: string; // required if name is provided
200+
userConfirmed: boolean;
201+
name?: string; // override relation
202+
displayName?: string; // override relation label
203+
selectedRecordId?: Array<string|number>; // override selected record (min 1 element)
194204
}
195205
```
196206

197-
Response: `204 No Content`.
198-
199-
The frontend calls this endpoint **before** `POST /runs/:runId/trigger`. On the next poll, `userConfirmed: true` and the executor reads `selectedRecordId` from the RunStore.
207+
### Responses
200208

201-
### mcp-task β€” user confirmation only
202-
203-
No payload required from the frontend. The executor selects the tool and writes `pendingData` itself (tool name + input) to the RunStore. The frontend just confirms:
204-
205-
```
206-
POST /runs/:runId/trigger
207-
```
208-
209-
The executor resumes with `userConfirmed: true` and executes the pre-selected tool.
209+
| Status | Meaning |
210+
|---|---|
211+
| `204 No Content` | Pending data updated successfully |
212+
| `400` | Invalid body β€” type mismatch, unknown fields, or empty `selectedRecordId` |
213+
| `404` | Step not found, no `pendingData`, or step type does not support confirmation |
210214

211215
---
212216

@@ -222,11 +226,19 @@ Orchestrator ──► GET pending?runId=X ──► Executor
222226
β”‚ β”‚
223227
status: awaiting-input POST /complete
224228
β”‚ (StepOutcome)
225-
Frontend writes pendingData
226-
to executor HTTP server TODO: route TBD
229+
β”‚
230+
Executor writes pendingData
231+
to RunStore (AI selection)
232+
β”‚
233+
Frontend reads pendingData
234+
via GET /runs/:runId
235+
β”‚
236+
Frontend overrides + confirms
237+
PATCH /runs/:runId/steps/:stepIndex/pending-data
238+
{ userConfirmed: true/false } β†’ 204
227239
β”‚
228240
POST /runs/:runId/trigger
229-
(next poll: userConfirmed = true)
230241
β”‚
231242
Executor resumes
243+
(reads userConfirmed from pendingData)
232244
```

0 commit comments

Comments
Β (0)