fix(flows): default require_approval to false for builder-created workflows (frontend paths) (#4668)

This commit is contained in:
Cyrus Gray
2026-07-08 01:15:32 +05:30
committed by GitHub
parent cd0b08ec0a
commit 8018eeb6ef
3 changed files with 16 additions and 15 deletions
@@ -35,15 +35,15 @@ describe('WorkflowPromptBar', () => {
// The created flow is the standard blank graph (single manual trigger).
expect(graph.nodes).toHaveLength(1);
expect(graph.nodes[0].kind).toBe('trigger');
// Parity with the proposal-card path: prompt-authored flows must default
// to requiring approval, matching WorkflowProposalCard's default.
expect(requireApproval).toBe(true);
// Prompt-authored flows default to NOT requiring approval, so a
// Run-button/scheduled run doesn't deadlock on an unsurfaceable approval.
expect(requireApproval).toBe(false);
expect(navigateMock).toHaveBeenCalledWith('/flows/flow-1', {
state: { copilotBuild: { description: 'digest my Slack every morning' } },
});
});
it('defaults requireApproval to true so outbound side-effects need explicit approval', async () => {
it('defaults requireApproval to false so runs do not deadlock on an unsurfaceable approval', async () => {
render(<WorkflowPromptBar />);
fireEvent.change(screen.getByTestId('workflow-prompt-input'), {
target: { value: 'auto-reply to every gmail thread' },
@@ -51,9 +51,9 @@ describe('WorkflowPromptBar', () => {
fireEvent.click(screen.getByTestId('workflow-prompt-submit'));
await waitFor(() => expect(createFlowMock).toHaveBeenCalledTimes(1));
// Third positional arg must be `true` — flows_create's server default is
// `false`, so omitting it would silently disarm the approval gate.
expect(createFlowMock.mock.calls[0][2]).toBe(true);
// Third positional arg must be `false` — prompt-authored flows default to
// not requiring approval so a Run-button/scheduled run can execute.
expect(createFlowMock.mock.calls[0][2]).toBe(false);
});
it('submits on Enter (Shift+Enter reserved for newlines)', async () => {
@@ -44,13 +44,14 @@ export default function WorkflowPromptBar({ variant = 'compact', autoFocus = fal
const name = deriveWorkflowName(trimmed, t('flows.page.newWorkflow'));
log('submit: creating flow name=%s', name);
try {
// Safe default: prompt-authored flows require approval so outbound
// Slack/Gmail/HTTP/code nodes cannot fire unattended. Omitting this
// arg would fall back to the server default of `false`.
// Prompt-authored flows default to NOT requiring approval — a
// Run-button/scheduled run has no chat context, so a parked approval
// card can't surface and the run would deadlock. The user can still
// turn approval on per-flow afterwards.
const flow = await createFlow(
name,
createBlankWorkflowGraph(name, t('flows.nodeKind.trigger')),
true
false
);
log('submit: created id=%s — opening canvas with build seed', flow.id);
navigate(`/flows/${flow.id}`, { state: { copilotBuild: { description: trimmed } } });
+4 -4
View File
@@ -324,10 +324,10 @@ function parseWorkflowProposal(output: string): WorkflowProposal | null {
return {
name: obj.name,
graph: obj.graph,
// The Rust tool defaults `require_approval` to `true` when the caller
// omits it, so treat anything other than an explicit `false` as `true`
// here too — keeps the client's fallback in lockstep with the server's.
requireApproval: obj.require_approval !== false,
// require_approval defaults to `false` — a proposal only requires approval
// if the builder explicitly set it so treat anything other than an
// explicit `true` as `false`, in lockstep with the server default.
requireApproval: obj.require_approval === true,
summary: { trigger: typeof summary.trigger === 'string' ? summary.trigger : '', steps },
};
}