From f325ccaed9284cd9519faaa9b773263b60525df7 Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Mon, 29 Jun 2026 16:14:38 +0100 Subject: [PATCH 1/5] feat(home): add auto-run toggle to quick actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an `auto` flag to `WorkflowAction` and an "Auto-run" toggle in the quick-action editor. When enabled, the server-side classifier (evaluate-code-workstreams worker) auto-runs the action as a cloud task whenever a workstream's primary situation matches the binding — gated so it never piles onto a workstream that already has a running task. This change is the config/UI half (harmless until the backend honors the flag): the `auto` field round-trips through the workflow config bindings, the editor exposes a Switch, and auto-enabled actions show a lightning glyph on the config map. Backend auto-run lands in posthog/posthog. Generated-By: PostHog Code Task-Id: e40b189a-6d1a-4de5-a228-79e681402b35 --- packages/core/src/workflow/schemas.ts | 4 ++++ .../home/config/ActionEditorPanel.tsx | 22 ++++++++++++++++++- .../features/home/config/SituationStation.tsx | 8 +++++-- .../home/stores/workflowEditorStore.test.ts | 9 ++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/core/src/workflow/schemas.ts b/packages/core/src/workflow/schemas.ts index 4488197cdb..cd01d113c4 100644 --- a/packages/core/src/workflow/schemas.ts +++ b/packages/core/src/workflow/schemas.ts @@ -61,6 +61,10 @@ export const workflowAction = z prompt: z.string().min(1).max(8_000), adapter: z.enum(["claude", "codex"]).optional(), model: z.string().min(1).optional(), + // When true, the server-side classifier auto-runs this action as a cloud + // task whenever a workstream's primary situation matches the binding (gated + // so it never piles onto a workstream that already has a running task). + auto: z.boolean().optional(), }) .strict(); export type WorkflowAction = z.infer; diff --git a/packages/ui/src/features/home/config/ActionEditorPanel.tsx b/packages/ui/src/features/home/config/ActionEditorPanel.tsx index a9b91f1581..29cd097e0c 100644 --- a/packages/ui/src/features/home/config/ActionEditorPanel.tsx +++ b/packages/ui/src/features/home/config/ActionEditorPanel.tsx @@ -12,7 +12,7 @@ import { useSettingsStore } from "@posthog/ui/features/settings/settingsStore"; import { usePreviewConfig } from "@posthog/ui/features/task-detail/hooks/usePreviewConfig"; import { Combobox } from "@posthog/ui/primitives/combobox/Combobox"; import type { ComboboxSearchKeys } from "@posthog/ui/primitives/combobox/useComboboxFilter"; -import { Card, Flex, Text, TextArea, TextField } from "@radix-ui/themes"; +import { Card, Flex, Switch, Text, TextArea, TextField } from "@radix-ui/themes"; import { useMemo } from "react"; import { SITUATION_TONE } from "./workflowMapLayout"; @@ -189,6 +189,26 @@ export function ActionEditorPanel({ )} + + + + patch({ auto: checked })} + aria-label="Auto-run this action" + /> + + Run automatically when a workstream enters this state + + + {action.auto ? ( + + A cloud task starts on its own — skipped while the workstream + already has a running task. + + ) : null} + diff --git a/packages/ui/src/features/home/config/SituationStation.tsx b/packages/ui/src/features/home/config/SituationStation.tsx index 454d8d8ba5..cc958b7717 100644 --- a/packages/ui/src/features/home/config/SituationStation.tsx +++ b/packages/ui/src/features/home/config/SituationStation.tsx @@ -1,4 +1,4 @@ -import { Plus, Sparkle } from "@phosphor-icons/react"; +import { Lightning, Plus, Sparkle } from "@phosphor-icons/react"; import { SITUATIONS, type SituationId, @@ -111,7 +111,11 @@ export function SituationStation({ id, bindings }: Props) { : action.label } > - + {action.auto ? ( + + ) : ( + + )} {action.label || "(no label)"} ); diff --git a/packages/ui/src/features/home/stores/workflowEditorStore.test.ts b/packages/ui/src/features/home/stores/workflowEditorStore.test.ts index dbb736d5d9..50266d72d7 100644 --- a/packages/ui/src/features/home/stores/workflowEditorStore.test.ts +++ b/packages/ui/src/features/home/stores/workflowEditorStore.test.ts @@ -117,6 +117,15 @@ describe("workflowEditorStore", () => { expect(store().draft?.bindings.working[0].label).toBe("Review"); expect(store().dirty).toBe(false); }); + + it("toggles the auto flag and marks dirty", () => { + store().beginEdit(makeConfig({ ci_failing: [makeAction({ id: "a" })] })); + store().updateAction("ci_failing", "a", { auto: true }); + expect(store().draft?.bindings.ci_failing[0].auto).toBe(true); + expect(store().dirty).toBe(true); + store().updateAction("ci_failing", "a", { auto: false }); + expect(store().draft?.bindings.ci_failing[0].auto).toBe(false); + }); }); describe("removeAction", () => { From 0f67899b2a20dbc82157c8adf1521d13e8382a1d Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Mon, 29 Jun 2026 16:26:06 +0100 Subject: [PATCH 2/5] feat(home): recognize action_auto_not_bool validation code Keeps the frontend validationDiagnostic enum in sync with the backend workflow-config validator, which now rejects a non-boolean `auto` flag. The save-result parse is strict, so the code must be enumerated here or a returned diagnostic would fail to parse. Generated-By: PostHog Code Task-Id: e40b189a-6d1a-4de5-a228-79e681402b35 --- packages/core/src/workflow/schemas.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/workflow/schemas.ts b/packages/core/src/workflow/schemas.ts index cd01d113c4..e548c4d1de 100644 --- a/packages/core/src/workflow/schemas.ts +++ b/packages/core/src/workflow/schemas.ts @@ -94,6 +94,7 @@ export const validationDiagnostic = z "duplicate_action_id", "action_empty_prompt", "action_empty_label", + "action_auto_not_bool", ]), message: z.string(), situationId: situationId.optional(), From fa98a226f60420acae8085fad51c5a864bc3fa6c Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Mon, 29 Jun 2026 18:00:58 +0100 Subject: [PATCH 3/5] refactor(home): simplify auto-run editor field and fix Biome formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Collapse the Auto-run Field to a single helper-text line with a state ternary, matching the Skill/Model field pattern (drops the one-off Flex wrapper and inline-label Text). - Document that `action_auto_not_bool` is emitted only by server-side save validation, so it isn't mistaken for dead surface and removed (it must stay listed for server-returned diagnostics to parse via saveResult). - Apply Biome formatting the original commits missed (multi-line @radix-ui/themes import, wrapped Lightning element) — fixes the red `quality` check. Generated-By: PostHog Code Task-Id: e40b189a-6d1a-4de5-a228-79e681402b35 --- packages/core/src/workflow/schemas.ts | 2 + .../home/config/ActionEditorPanel.tsx | 38 ++++++++++--------- .../features/home/config/SituationStation.tsx | 6 ++- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/packages/core/src/workflow/schemas.ts b/packages/core/src/workflow/schemas.ts index e548c4d1de..72a173cbad 100644 --- a/packages/core/src/workflow/schemas.ts +++ b/packages/core/src/workflow/schemas.ts @@ -94,6 +94,8 @@ export const validationDiagnostic = z "duplicate_action_id", "action_empty_prompt", "action_empty_label", + // Emitted only by server-side save validation (not the client validator); + // listed here so server-returned diagnostics still parse via saveResult. "action_auto_not_bool", ]), message: z.string(), diff --git a/packages/ui/src/features/home/config/ActionEditorPanel.tsx b/packages/ui/src/features/home/config/ActionEditorPanel.tsx index 29cd097e0c..f31dd61dba 100644 --- a/packages/ui/src/features/home/config/ActionEditorPanel.tsx +++ b/packages/ui/src/features/home/config/ActionEditorPanel.tsx @@ -12,7 +12,14 @@ import { useSettingsStore } from "@posthog/ui/features/settings/settingsStore"; import { usePreviewConfig } from "@posthog/ui/features/task-detail/hooks/usePreviewConfig"; import { Combobox } from "@posthog/ui/primitives/combobox/Combobox"; import type { ComboboxSearchKeys } from "@posthog/ui/primitives/combobox/useComboboxFilter"; -import { Card, Flex, Switch, Text, TextArea, TextField } from "@radix-ui/themes"; +import { + Card, + Flex, + Switch, + Text, + TextArea, + TextField, +} from "@radix-ui/themes"; import { useMemo } from "react"; import { SITUATION_TONE } from "./workflowMapLayout"; @@ -191,23 +198,18 @@ export function ActionEditorPanel({ - - patch({ auto: checked })} - aria-label="Auto-run this action" - /> - - Run automatically when a workstream enters this state - - - {action.auto ? ( - - A cloud task starts on its own — skipped while the workstream - already has a running task. - - ) : null} + patch({ auto: checked })} + aria-label="Auto-run this action" + /> + + {action.auto + ? "A cloud task starts on its own — skipped while the workstream already has a running task." + : "Run automatically when a workstream enters this state."} + diff --git a/packages/ui/src/features/home/config/SituationStation.tsx b/packages/ui/src/features/home/config/SituationStation.tsx index cc958b7717..8a4ea8d98f 100644 --- a/packages/ui/src/features/home/config/SituationStation.tsx +++ b/packages/ui/src/features/home/config/SituationStation.tsx @@ -112,7 +112,11 @@ export function SituationStation({ id, bindings }: Props) { } > {action.auto ? ( - + ) : ( )} From a1c705d5c28ca133742e872da13bad65510df81d Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Tue, 30 Jun 2026 15:18:31 +0100 Subject: [PATCH 4/5] fix(home): clear dirty flag when auto-run is toggled off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Toggling auto-run off wrote an explicit `auto: false`, which serializes differently from the baseline (which has no `auto` key), so an enable → disable round-trip left `dirty === true` and falsely prompted an unsaved-changes save. The editor now sends `checked || undefined`, keeping the field absent when off so the serialization matches baseline and the dirty flag clears. Adds a store test covering the enable → disable round-trip clears dirty. Addresses Greptile review feedback on the PR. Generated-By: PostHog Code Task-Id: e40b189a-6d1a-4de5-a228-79e681402b35 --- .../src/features/home/config/ActionEditorPanel.tsx | 4 +++- .../home/stores/workflowEditorStore.test.ts | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/features/home/config/ActionEditorPanel.tsx b/packages/ui/src/features/home/config/ActionEditorPanel.tsx index f31dd61dba..cab611fffe 100644 --- a/packages/ui/src/features/home/config/ActionEditorPanel.tsx +++ b/packages/ui/src/features/home/config/ActionEditorPanel.tsx @@ -202,7 +202,9 @@ export function ActionEditorPanel({ className="self-start" size="1" checked={action.auto ?? false} - onCheckedChange={(checked) => patch({ auto: checked })} + onCheckedChange={(checked) => + patch({ auto: checked || undefined }) + } aria-label="Auto-run this action" /> diff --git a/packages/ui/src/features/home/stores/workflowEditorStore.test.ts b/packages/ui/src/features/home/stores/workflowEditorStore.test.ts index 50266d72d7..61a877be2d 100644 --- a/packages/ui/src/features/home/stores/workflowEditorStore.test.ts +++ b/packages/ui/src/features/home/stores/workflowEditorStore.test.ts @@ -126,6 +126,19 @@ describe("workflowEditorStore", () => { store().updateAction("ci_failing", "a", { auto: false }); expect(store().draft?.bindings.ci_failing[0].auto).toBe(false); }); + + it("clears dirty when auto is toggled on then back off", () => { + // The editor sends `checked || undefined`, so disabling clears the key + // rather than writing `auto: false`. That restores the exact baseline + // serialization (no `auto` key), so the dirty flag must return to false — + // otherwise an enable→disable round-trip falsely prompts an unsaved-changes save. + store().beginEdit(makeConfig({ ci_failing: [makeAction({ id: "a" })] })); + store().updateAction("ci_failing", "a", { auto: true }); + expect(store().dirty).toBe(true); + store().updateAction("ci_failing", "a", { auto: undefined }); + expect(store().draft?.bindings.ci_failing[0].auto).toBeUndefined(); + expect(store().dirty).toBe(false); + }); }); describe("removeAction", () => { From 088ef900be627b13430e9804f619d154c22f177c Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Tue, 30 Jun 2026 15:21:10 +0100 Subject: [PATCH 5/5] chore(home): drop redundant comments in workflow schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the explanatory comments on the `auto` field and the `action_auto_not_bool` diagnostic code per review feedback — the intent is clear from the field/code names. Generated-By: PostHog Code Task-Id: e40b189a-6d1a-4de5-a228-79e681402b35 --- packages/core/src/workflow/schemas.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/core/src/workflow/schemas.ts b/packages/core/src/workflow/schemas.ts index 72a173cbad..e5baf88eff 100644 --- a/packages/core/src/workflow/schemas.ts +++ b/packages/core/src/workflow/schemas.ts @@ -61,9 +61,6 @@ export const workflowAction = z prompt: z.string().min(1).max(8_000), adapter: z.enum(["claude", "codex"]).optional(), model: z.string().min(1).optional(), - // When true, the server-side classifier auto-runs this action as a cloud - // task whenever a workstream's primary situation matches the binding (gated - // so it never piles onto a workstream that already has a running task). auto: z.boolean().optional(), }) .strict(); @@ -94,8 +91,6 @@ export const validationDiagnostic = z "duplicate_action_id", "action_empty_prompt", "action_empty_label", - // Emitted only by server-side save validation (not the client validator); - // listed here so server-returned diagnostics still parse via saveResult. "action_auto_not_bool", ]), message: z.string(),