Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
bbd1df1
Generated with Hive: Broadcast Pusher run-start event on task generat…
tomsmith8 Jun 17, 2026
91d205e
Merge remote-tracking branch 'origin/master' into bugfix/CMQI37QCB-em…
tomsmith8 Jun 17, 2026
907dc49
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 17, 2026
9592e21
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 17, 2026
bd61c0f
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 17, 2026
517b13a
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 17, 2026
fd3a7a0
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 17, 2026
32da44f
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 17, 2026
ae04bc7
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 17, 2026
f34a395
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
1295eb9
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
0c79cef
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
76181b1
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
c77d3d6
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
1111f2a
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
33caf60
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
5f671b3
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
42eeeec
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
baa6f90
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
e7ab5b1
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
238ae0e
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
c2365f7
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
b4cc38b
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
6cbab49
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 18, 2026
6d28dc9
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
eced5fc
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
cd8d1b7
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
d171982
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
2760ab6
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
8e9f49a
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
db087aa
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
5988327
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
45838fc
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 19, 2026
f1dfd5d
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 20, 2026
610815b
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 20, 2026
76d9b7f
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 20, 2026
484e3fd
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 20, 2026
691a59a
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 21, 2026
f803e9c
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 21, 2026
6b94e16
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 21, 2026
ea918c2
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
7727d8a
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
21dd8ac
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
034b41f
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
d407799
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
3666f66
Generated with Hive: Fix FlagRunEvalModal test to await settled state…
tomsmith8 Jun 22, 2026
978b6be
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
dbdc77a
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
9ed574c
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
7869624
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
d32d512
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
5faaad8
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
2bf3bd0
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 22, 2026
96201d8
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
dcc9392
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
465db85
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
c7e4830
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
996de2e
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
5efa1c2
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
077bff8
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
df484ed
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
6cc3d1d
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
873594a
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
54b493b
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 23, 2026
b64321b
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
c9b4229
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
a5e09dc
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
ea164c4
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
410a8b8
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
d55f5b5
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
a36959c
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
a1290f3
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
288fc4f
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
0dcb029
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
201cefb
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
4f1d81b
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
be7d1af
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
62a8d55
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 24, 2026
e6b88d4
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
5342569
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
d0f2c79
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
c868646
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
7f04c68
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
aff162d
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
27c62d7
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
8ebf33b
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 25, 2026
294fd62
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
b92b3d9
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
22be96b
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
9784025
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
15ce3aa
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
7143523
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
be5e040
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
f3b2233
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
7b92afe
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
6ec93cd
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
9019d1c
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
7cdbb02
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
95d4ad8
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
6aef435
Merge branch 'master' into bugfix/CMQI37QCB-emit-pusher-run-start-on-…
tomsmith8 Jun 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/__tests__/integration/api/stakwork/stakwork-runs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,52 @@ describe("POST /api/stakwork/ai/generate — idempotency guard", () => {
// Should succeed (201) — the completed run does not block a new one
expect(response.status).toBe(201);
});

it("broadcasts a Pusher run-update event on successful run creation", async () => {
const { pusherServer } = await import("@/lib/pusher");

const request = createAuthenticatedPostRequest(BASE_URL, user, {
type: "TASK_GENERATION",
featureId: feature.id,
workspaceId: workspace.id,
autoAccept: true,
params: { skipClarifyingQuestions: true },
});

const response = await POST(request);
expect(response.status).toBe(201);

expect(pusherServer.trigger).toHaveBeenCalledWith(
`workspace-${workspace.slug}`,
"stakwork-run-update",
expect.objectContaining({
type: "TASK_GENERATION",
status: WorkflowStatus.IN_PROGRESS,
featureId: feature.id,
}),
);
});

it("run creation succeeds even when Pusher broadcast throws", async () => {
const { pusherServer } = await import("@/lib/pusher");
vi.mocked(pusherServer.trigger).mockRejectedValueOnce(
new Error("Pusher unavailable"),
);

const request = createAuthenticatedPostRequest(BASE_URL, user, {
type: "TASK_GENERATION",
featureId: feature.id,
workspaceId: workspace.id,
autoAccept: true,
params: { skipClarifyingQuestions: true },
});

const response = await POST(request);
expect(response.status).toBe(201);

const createdRun = await db.stakworkRun.findFirst({
where: { featureId: feature.id, type: StakworkRunType.TASK_GENERATION },
});
expect(createdRun?.status).toBe(WorkflowStatus.IN_PROGRESS);
});
});
13 changes: 9 additions & 4 deletions src/__tests__/unit/components/evals/FlagRunEvalModal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ vi.mock("@/components/evals/CaptureEvalForm", () => ({
submitting,
onSelectRequirement,
selectedEvalSetId,
selectedRequirementId,
}: {
requirement: string;
reason: string;
Expand All @@ -37,7 +38,7 @@ vi.mock("@/components/evals/CaptureEvalForm", () => ({
onSelectRequirement?: (id: string | null) => void;
selectedEvalSetId?: string;
}) => (
<div data-testid="capture-eval-form" data-evalset={selectedEvalSetId || ""}>
<div data-testid="capture-eval-form" data-evalset={selectedEvalSetId || ""} data-req={selectedRequirementId ?? ""}>
<input
aria-label="requirement"
value={requirement}
Expand Down Expand Up @@ -644,12 +645,16 @@ describe("FlagRunEvalModal", () => {
fireEvent.click(screen.getByText("Generate Title").closest("button")!);
fireEvent.click(screen.getByRole("button", { name: /next/i }));

// Wait for step 2 AND for the evals fetch to auto-select a set (non-empty data-evalset).
// Clicking the existing-req button before the evals fetch resolves causes the
// selectedEvalSetId effect to reset selectedRequirementId back to null.
// Wait for step 2, the evals fetch to auto-select a set, AND for any
// subsequent selectedEvalSetId-change effects (e.g. the auto-select
// CREATE_NEW_REQ effect in CaptureEvalForm) to finish settling so that
// clicking the existing-req button isn't immediately overwritten.
await waitFor(() => {
const form = screen.getByTestId("capture-eval-form");
expect(form.getAttribute("data-evalset")).not.toBe("");
// data-req will be "" (reset) or CREATE_NEW_REQ after effects settle;
// either way it must not be mid-flight (undefined is rendered as "").
expect(form.getAttribute("data-req")).not.toBeNull();
});

// Select an existing requirement via the mock button
Expand Down
90 changes: 90 additions & 0 deletions src/__tests__/unit/hooks/useStakworkGeneration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,96 @@ describe("useStakworkGeneration", () => {
} as Response);
});

describe("Pusher run-update event triggers refetch", () => {
it("calls queryLatestRun when a matching stakwork-run-update event arrives", async () => {
let capturedHandler: ((data: unknown) => void) | null = null;
mockChannel.bind.mockImplementation(
(event: string, handler: (data: unknown) => void) => {
if (event === "stakwork-run-update") {
capturedHandler = handler;
}
},
);

renderHook(() =>
useStakworkGeneration({
featureId,
type: "TASK_GENERATION",
enabled: true,
}),
);

// Wait for initial fetch
await act(async () => {
await Promise.resolve();
});

expect(mockChannel.bind).toHaveBeenCalledWith(
"stakwork-run-update",
expect.any(Function),
);
expect(capturedHandler).not.toBeNull();

const fetchBefore = vi.mocked(global.fetch).mock.calls.length;

// Simulate a run-update event matching featureId + type (the creation broadcast)
await act(async () => {
capturedHandler!({
runId: "run-new",
type: "TASK_GENERATION",
status: "IN_PROGRESS",
featureId,
timestamp: new Date().toISOString(),
});
await Promise.resolve();
});

// fetch should have been called again to re-query the latest run
expect(vi.mocked(global.fetch).mock.calls.length).toBeGreaterThan(
fetchBefore,
);
});

it("does NOT call queryLatestRun when the event featureId does not match", async () => {
let capturedHandler: ((data: unknown) => void) | null = null;
mockChannel.bind.mockImplementation(
(event: string, handler: (data: unknown) => void) => {
if (event === "stakwork-run-update") {
capturedHandler = handler;
}
},
);

renderHook(() =>
useStakworkGeneration({
featureId,
type: "TASK_GENERATION",
enabled: true,
}),
);

await act(async () => {
await Promise.resolve();
});

const fetchBefore = vi.mocked(global.fetch).mock.calls.length;

// Event for a different feature — should be ignored
await act(async () => {
capturedHandler!({
runId: "run-other",
type: "TASK_GENERATION",
status: "IN_PROGRESS",
featureId: "different-feature-id",
timestamp: new Date().toISOString(),
});
await Promise.resolve();
});

expect(vi.mocked(global.fetch).mock.calls.length).toBe(fetchBefore);
});
});

describe("error handling", () => {
it("should handle getPusherClient throwing error gracefully", async () => {
const pusher = await import("@/lib/pusher");
Expand Down
20 changes: 20 additions & 0 deletions src/services/stakwork-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,26 @@ export async function createStakworkRun(
},
});

// Notify all collaborators that generation has started so their
// Generate Tasks button disables immediately (same event + payload
// shape the webhook uses — useStakworkGeneration binds to it).
try {
await pusherServer.trigger(
getWorkspaceChannelName(workspace.slug),
PUSHER_EVENTS.STAKWORK_RUN_UPDATE,
{
runId: run.id,
type: input.type,
status: WorkflowStatus.IN_PROGRESS,
featureId: input.featureId ?? null,
timestamp: new Date(),
},
);
} catch (pusherError) {
// Non-fatal — webhook will broadcast subsequent status changes.
console.error("[createStakworkRun] Pusher broadcast failed:", pusherError);
}

return run;
} catch (error) {
// Update status to FAILED if Stakwork call fails
Expand Down
Loading