Files
paseo/packages/app/e2e/browser/creation-idempotency.spec.ts
Mohamed Boudra d63132a37d fix(creation): prevent duplicate workspaces and agents on retries (#4442)
* fix(creation): prevent duplicate workspaces and agents on retries

* test(creation): cover plugin hooks across receipt replays

* fix(creation): recover draft retries without accepting partial workspaces

* refactor(creation): name receipt inputs and tag compatibility gate

* fix(file-observer): track files announced by native change events

* fix(creation): keep initial prompts in daemon-owned creation requests

* fix(creation): reuse connection-owned progress and recovery

* test(creation): adapt handoff gates to combined requests

* ci(desktop): avoid xz compression for smoke packages

* ci(desktop): pass valid RPM compression through FPM config

* refactor(creation): give legacy and modern requests one identity owner

* fix(creation): log observer failures without interrupting creation
2026-09-14 16:55:42 +02:00

131 lines
4.8 KiB
TypeScript

import type { createCreationScenario } from "../support/helpers/creation";
import { expect } from "../support/fixtures";
import { test } from "../support/creation-fixtures";
for (const isolation of ["local", "worktree"] as const) {
test(`repeated Create clicks before a render create only one ${isolation} workspace`, async ({
creation,
}) => {
await creation.openWorkspaceForm(isolation);
await creation.submitRepeatedly("Create");
await creation.expectOneCreatedWorkspace();
});
}
test("repeated Send clicks before a render create only one agent", async ({ creation }) => {
await creation.openAgentDraft();
await creation.submitRepeatedly("Send message", "Start exactly one agent for this prompt.");
await creation.expectPromptVisible();
await creation.expectAgentCount(1);
});
test("retrying the app's agent creation returns the same agent for every attempt", async ({
creation,
agentRetries,
}) => {
await creation.openAgentDraft();
await creation.submitPrompt("Start exactly one agent for this prompt.");
expect(new Set(await agentRetries.completedAgentIds()).size).toBe(1);
await creation.expectAgentCount(1);
});
test("the first prompt still names an agent created with a receipt", async ({ creation }) => {
await creation.openAgentDraft();
await creation.submitPrompt("Name this new agent from its first prompt.");
await creation.expectPromptVisible();
await creation.expectAgentTitle("Name this new agent from its first prompt.");
});
test("reconnecting after a lost creation response recovers the agent and initial message", async ({
creation,
promptRetry,
}) => {
await creation.openAgentDraft();
promptRetry.holdAcknowledgement();
await creation.submitPrompt("Deliver this initial prompt once.");
await promptRetry.waitForDeliveredPrompt();
await promptRetry.disconnectAndReconnect();
await promptRetry.expectSameAgentAndMessage();
await creation.expectAgentCount(1);
});
test("a remounted draft reconciles its original creation after reconnect", async ({
creation,
promptRetry,
}) => {
await creation.openAgentDraft();
promptRetry.holdAcknowledgement();
await creation.submitPrompt("Deliver this initial prompt once.");
await promptRetry.waitForDeliveredPrompt();
await creation.evictAndReturnToDraft();
await creation.expectPromptVisible("Deliver this initial prompt once.");
await promptRetry.disconnectAndReconnect();
await promptRetry.expectSameAgentAndMessage();
await creation.expectAgentCount(1);
});
test("repeated new-workspace prompt submissions create one workspace and one agent", async ({
creation,
}) => {
await creation.openWorkspaceForm("local");
await creation.submitRepeatedly("Create", "Start one agent in one new workspace.");
await creation.expectPromptVisible();
await creation.expectOneCreatedWorkspace();
await creation.expectAgentCount(1);
});
test("separate drafts can intentionally create two agents in the same workspace", async ({
creation,
delayedCreation,
}) => {
await creation.openAgentDraft();
await creation.submitPrompt("Start the first agent.");
await creation.expectPromptVisible("Start the first agent.");
await delayedCreation.waitForDelayedCreatedStatus();
await creation.startAnotherDraft();
await creation.submitPrompt("Start the second agent.");
await creation.expectPromptVisible("Start the second agent.");
await delayedCreation.waitForDelayedCreatedStatus();
delayedCreation.release();
await creation.expectAgentCount(2);
});
test("new workspace navigation and optimistic prompt precede agent initialization", async ({
creation,
startup,
delayedCreation,
}) => {
await creation.openWorkspaceForm("worktree");
await creation.submitPrompt("Show this prompt while the agent is starting.", "Create");
await creation.expectAgentStillStarting();
delayedCreation.expectSingleWorkspaceIntent();
delayedCreation.release();
await startup.release();
await creation.expectOneCreatedWorkspace();
await creation.expectAgentCount(1);
});
for (const scenario of [
{ suffix: "", prepare: async () => {} },
{
suffix: " after remount",
prepare: async (creation: Awaited<ReturnType<typeof createCreationScenario>>) =>
creation.evictAndReturnToDraft(),
},
]) {
test(`retrying failed agent initialization preserves its workspace${scenario.suffix}`, async ({
creation,
startup,
}) => {
await creation.openWorkspaceForm("local");
await creation.submitPrompt("Retry this workspace and agent together.", "Create");
await creation.expectAgentStillStarting();
await startup.fail();
await creation.expectStartupFailure();
await creation.expectOneCreatedWorkspace();
await scenario.prepare(creation);
await creation.submitPrompt("Retry this workspace and agent together.");
await creation.expectAgentCount(1);
});
}