Make public starter channels best effort (#5192)

## Summary
- treat public starter-channel provisioning as best-effort after
preserving the required private Welcome path
- let community onboarding complete and focus Welcome when the reported
metadata lookup error occurs
- remove the now-obsolete retry-toast expectations for optional starter
provisioning

## Scope
This intentionally does not change relay tombstone semantics or
auto-join existing public channels.

## Test plan
- `pnpm exec playwright test tests/e2e/deep-link-invite.spec.ts` (8
passed)
- `pnpm exec playwright test tests/e2e/onboarding.spec.ts --grep "failed
public starter channel setup"` (1 passed)
- `pnpm typecheck`
- `pnpm check`
- `pnpm test` (4483 passed)
- pre-push hook: branch-skew, desktop-check, desktop-typecheck,
desktop-test passed on `4658a07beb1e1d54443da5cd2e4a28fae0232f24`

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
This commit is contained in:
Wes
2026-08-07 09:21:29 -07:00
committed by GitHub
co-authored by Carl
parent 8476ea05e7
commit daa8877581
3 changed files with 75 additions and 138 deletions
+3 -12
View File
@@ -82,14 +82,15 @@ export async function initializeStarterChannels(
let starterChannels: Awaited<
ReturnType<typeof ensureStarterChannels>
> | null = null;
let starterChannelsError: unknown = null;
try {
starterChannels = await ensureStarterChannels({
ensureStarterChannels: ensureStarterChannelsCommand,
getChannels,
});
} catch (error) {
starterChannelsError = error;
// Public starter channels are optional. Owners may have deliberately
// deleted their deterministic starter channels; that must not strand a
// new member after the required private Welcome channel succeeds.
console.warn("Failed to initialize public starter channels.", error);
}
@@ -145,16 +146,6 @@ export async function initializeStarterChannels(
notifyWelcomeChannelReady(welcomeChannel.id);
}
const focusChannelId = focus ? welcomeChannel.id : undefined;
if (starterChannelsError) {
return {
ok: false,
focusChannelId,
reason:
starterChannelsError instanceof Error
? starterChannelsError.message
: "Failed to set up starter channels",
};
}
return { ok: true, focusChannelId };
} catch (error) {
console.warn("Failed to initialize starter channels.", error);
+65 -63
View File
@@ -8,7 +8,7 @@ import { seedActiveIdentity } from "../helpers/onboarding";
// Invite claiming waits until setup finishes and the final identity is known.
const DEFAULT_MOCK_PUBKEY = "deadbeef".repeat(8);
const WELCOME_FAILURE_PUBKEY = TEST_IDENTITIES.tyler.pubkey;
const COMMUNITY_ONBOARDING_PUBKEY = TEST_IDENTITIES.tyler.pubkey;
const TRANSACTION_STORAGE_KEY = "buzz-community-onboarding-transaction.v1";
const COMMUNITY_RELAY_URL = "wss://hive.example.com";
@@ -262,7 +262,63 @@ test("queued add-community links open and acknowledge one at a time", async ({
]);
});
test("Welcome failure retries once before allowing starter channel setup to be skipped", async ({
test("deleted public starter channels do not strand community onboarding", async ({
page,
}) => {
const starterError =
"starter channels created but metadata not yet available";
await seedActiveIdentity(page, TEST_IDENTITIES.tyler);
await page.addInitScript(
({ pubkey, relayUrl, storageKey }) => {
window.localStorage.setItem(
`buzz-machine-onboarding-complete.v2:${pubkey}`,
"true",
);
const timestamp = new Date().toISOString();
window.localStorage.setItem(
storageKey,
JSON.stringify({
id: "txn-deleted-starters-1",
source: "deep-link-join",
stage: "team-intro",
relayUrl,
communityName: "hive",
communityId: "e2e-default-community",
createdAt: timestamp,
updatedAt: timestamp,
}),
);
},
{
pubkey: COMMUNITY_ONBOARDING_PUBKEY,
relayUrl: COMMUNITY_RELAY_URL,
storageKey: TRANSACTION_STORAGE_KEY,
},
);
await installMockBridge(
page,
{ ensureStarterChannelsErrors: [starterError] },
{ relayWsUrl: COMMUNITY_RELAY_URL, skipOnboardingSeed: true },
);
await page.goto("/");
await page.getByRole("button", { name: "Take me to Buzz" }).click();
await expect(page.getByTestId("community-onboarding-flow")).toHaveCount(0);
await expect(page).toHaveURL(/#\/channels\/[^/]+$/);
await expect(page.getByTestId("chat-title")).toContainText("Welcome");
await expect(page.getByText(starterError)).toHaveCount(0);
expect(
await page.evaluate(
() =>
window.__BUZZ_E2E_COMMANDS__?.filter(
(command) => command === "ensure_starter_channels",
).length ?? 0,
),
).toBe(1);
});
test("required Welcome creation failure keeps community onboarding open", async ({
page,
}) => {
const welcomeError = "Channel creation is not permitted.";
@@ -289,80 +345,26 @@ test("Welcome failure retries once before allowing starter channel setup to be s
);
},
{
pubkey: WELCOME_FAILURE_PUBKEY,
pubkey: COMMUNITY_ONBOARDING_PUBKEY,
relayUrl: COMMUNITY_RELAY_URL,
storageKey: TRANSACTION_STORAGE_KEY,
},
);
await installMockBridge(
page,
{ ensureStarterChannelsErrors: [welcomeError, welcomeError, welcomeError] },
{ createChannelErrors: [welcomeError] },
{ relayWsUrl: COMMUNITY_RELAY_URL, skipOnboardingSeed: true },
);
await page.goto("/");
for (const name of ["fizz", "honey", "bumble"]) {
const character = page.getByTestId(`starter-persona-${name}`);
await expect(character).toBeVisible();
await expect(character).toHaveAttribute(
"src",
`/onboarding/starter-team/${name}.png`,
);
}
const enterButton = page.getByRole("button", { name: "Take me to Buzz" });
await enterButton.click();
await page.getByRole("button", { name: "Take me to Buzz" }).click();
await expect(page.getByTestId("community-onboarding-flow")).toBeVisible();
await expect(page.getByText(`${welcomeError} Try again.`)).toBeVisible();
await expect(enterButton).toBeEnabled();
const backButton = page.getByRole("button", { name: "Back" });
await expect(backButton).toBeVisible();
await backButton.click();
await expect(
page.getByRole("heading", { name: "Build your profile" }),
).toBeVisible();
await page.getByLabel("Community username").fill("Tyler");
await page.getByTestId("community-profile-next").click();
await enterButton.click();
await expect(page.getByText(`${welcomeError} Try again.`)).toBeVisible();
await expect(enterButton).toBeEnabled();
await enterButton.click();
const skipButton = page.getByRole("button", { name: "Skip for now" });
await expect(page.getByText(welcomeError, { exact: true })).toBeVisible();
await expect(skipButton).toBeEnabled();
await expect(page.getByRole("button", { name: "Back" })).toBeVisible();
const starterChannelAttempts = await page.evaluate(
() =>
window.__BUZZ_E2E_COMMANDS__?.filter(
(command) => command === "ensure_starter_channels",
).length ?? 0,
);
expect(starterChannelAttempts).toBe(3);
await skipButton.click();
await expect(page.getByTestId("community-onboarding-flow")).toHaveCount(0);
expect(
await page.evaluate(
() =>
window.__BUZZ_E2E_COMMANDS__?.filter(
(command) => command === "ensure_starter_channels",
).length ?? 0,
),
).toBe(3);
await expect
.poll(() =>
page.evaluate(
(transaction) => window.localStorage.getItem(transaction),
TRANSACTION_STORAGE_KEY,
),
)
.toBeNull();
page.getByRole("button", { name: "Take me to Buzz" }),
).toBeEnabled();
await expect(page.getByTestId("chat-title")).toHaveCount(0);
});
test("persisted deep-link invite hands off to Joining after machine onboarding", async ({
+7 -63
View File
@@ -2985,27 +2985,6 @@ test("first-run onboarding keeps the shell hidden and lands on private Welcome a
await expectWelcomeGuideIntro(page);
});
function retryToast(page: Page, title: string) {
return page
.locator("[data-sonner-toast][data-removed='false']")
.filter({ hasText: title });
}
async function retryToastAction(
page: Page,
{ command, title }: { command: string; title: string },
) {
const activeToast = retryToast(page, title);
await expect(
activeToast.getByRole("button", { name: "Retry" }),
).toBeVisible();
const before = await commandCount(page, command);
await activeToast
.getByRole("button", { name: "Retry" })
.dispatchEvent("click");
await expect.poll(() => commandCount(page, command)).toBeGreaterThan(before);
}
async function commandCount(page: Page, command: string) {
return page.evaluate(
(target) =>
@@ -3015,16 +2994,14 @@ async function commandCount(page: Page, command: string) {
);
}
test("failed starter channel retries recreate actionable toasts", async ({
test("failed public starter channel setup does not show a retry toast", async ({
page,
}) => {
const starterError = "Mock starter channel setup failed.";
await seedActiveIdentity(page, BLANK_TYLER_IDENTITY);
await installMockBridge(
page,
{
ensureStarterChannelsErrors: [starterError, starterError, starterError],
},
{ ensureStarterChannelsErrors: [starterError, starterError] },
{ skipOnboardingSeed: true },
);
await page.goto("/");
@@ -3033,45 +3010,12 @@ test("failed starter channel retries recreate actionable toasts", async ({
await completeProfileOnboarding(page);
await expectPrivateWelcomeLanding(page);
const title = "Couldn't set up starter channels";
const activeToast = retryToast(page, title);
await expect(activeToast).toContainText(starterError);
await retryToastAction(page, {
command: "ensure_starter_channels",
title,
});
await expect(activeToast).toContainText(starterError);
await expect(
activeToast.getByRole("button", { name: "Retry" }),
).toBeVisible();
});
test("successful starter channel retry clears its actionable toast", async ({
page,
}) => {
const starterError = "Mock starter channel setup failed.";
await seedActiveIdentity(page, BLANK_TYLER_IDENTITY);
await installMockBridge(
page,
{
ensureStarterChannelsErrors: [starterError, starterError],
},
{ skipOnboardingSeed: true },
);
await page.goto("/");
await page.getByTestId("onboarding-display-name").fill("Morty QA");
await completeProfileOnboarding(page);
const title = "Couldn't set up starter channels";
await expect(retryToast(page, title)).toContainText(starterError);
await retryToastAction(page, {
command: "ensure_starter_channels",
title,
});
await expect(retryToast(page, title)).toHaveCount(0);
await expectWelcomeView(page);
await expectStarterChannels(page);
page
.locator("[data-sonner-toast][data-removed='false']")
.filter({ hasText: "Couldn't set up starter channels" }),
).toHaveCount(0);
expect(await commandCount(page, "ensure_starter_channels")).toBe(2);
});
test("first-run onboarding posts the live Fizz kickoff", async ({ page }) => {