diff --git a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx
index 4a6beac325..16f8932b2a 100644
--- a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx
+++ b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx
@@ -145,4 +145,20 @@ describe("OnboardingPreviewCard", () => {
expect(screen.queryByText(/Here's what LoopOver would have flagged/)).toBeNull();
expect(apiFetch).not.toHaveBeenCalled();
});
+
+ it("migrates a pre-rebrand gittensory_ dismiss flag so the card stays hidden (#7782)", async () => {
+ window.localStorage.setItem(
+ "gittensory_maintainer_onboarding_preview_dismissed",
+ JSON.stringify({ dismissed: true }),
+ );
+ apiFetch.mockResolvedValue({ ok: true, data: preview() });
+ render();
+ await waitFor(() =>
+ expect(window.localStorage.getItem("loopover_maintainer_onboarding_preview_dismissed")).toBe(
+ JSON.stringify({ dismissed: true }),
+ ),
+ );
+ expect(screen.queryByText(/Here's what LoopOver would have flagged/)).toBeNull();
+ expect(apiFetch).not.toHaveBeenCalled();
+ });
});
diff --git a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx
index 7e754f20e3..3315794de9 100644
--- a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx
+++ b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx
@@ -19,7 +19,8 @@ type ReviewabilityRow = { pr: string; title: string; reason: string };
const DISMISS_KEY = "loopover_maintainer_onboarding_preview_dismissed";
// One-time rebrand migration fallback -- see useLocalStorage's legacyKey param.
-const LEGACY_DISMISS_KEY = "loopover_maintainer_onboarding_preview_dismissed";
+// Must stay as the pre-rebrand `gittensory_...` literal (distinct from DISMISS_KEY); #7782.
+const LEGACY_DISMISS_KEY = "gittensory_maintainer_onboarding_preview_dismissed";
/** Builds a settings-preview form from a REAL cached PR (title, and a linked-issue number scraped from
* `reason` when present) — everything else (author identity, labels, body) isn't in the reviewability
diff --git a/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx b/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx
index c5a42c53a1..a57253a523 100644
--- a/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx
+++ b/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx
@@ -1,5 +1,5 @@
-import { fireEvent, render, screen } from "@testing-library/react";
-import { describe, expect, it, vi } from "vitest";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
// #6985: a real fetch failure used to render the same generic text as "still loading" — these tests
// pin the three render paths (loading / error / success) now that LoadingState/ErrorState replace it.
@@ -31,6 +31,10 @@ const notificationModelFixture = {
};
describe("NotificationReadinessCard loading/error states (#6985)", () => {
+ beforeEach(() => {
+ window.localStorage.clear();
+ });
+
it("shows a LoadingState (not the generic spinner-free text) while the model loads", () => {
useApiResource.mockReturnValue({
status: "loading",
@@ -94,4 +98,20 @@ describe("NotificationReadinessCard loading/error states (#6985)", () => {
expect(container.textContent).toContain("No content leaves the browser without consent.");
expect(screen.queryByText("Loading notification model…")).toBeNull();
});
+
+ it("migrates a pre-rebrand gittensory_ opt-in flag so the pill shows enabled (#7782)", async () => {
+ window.localStorage.setItem("gittensory_notification_opt_in", JSON.stringify(true));
+ useApiResource.mockReturnValue({
+ status: "ready",
+ data: notificationModelFixture,
+ error: null,
+ loadedAt: Date.now(),
+ reload: () => {},
+ });
+
+ render();
+ await waitFor(() => expect(screen.getByText("opt-in enabled")).toBeTruthy());
+ expect(window.localStorage.getItem("loopover_notification_opt_in")).toBe(JSON.stringify(true));
+ expect(screen.queryByText("opt-in required")).toBeNull();
+ });
});
diff --git a/apps/loopover-ui/src/components/site/notification-readiness-card.tsx b/apps/loopover-ui/src/components/site/notification-readiness-card.tsx
index 36459045c2..d9a0698b1b 100644
--- a/apps/loopover-ui/src/components/site/notification-readiness-card.tsx
+++ b/apps/loopover-ui/src/components/site/notification-readiness-card.tsx
@@ -30,10 +30,11 @@ export function NotificationReadinessCard() {
"/v1/app/notification-model",
"Notification model",
);
+ // Legacy key must stay as the pre-rebrand `gittensory_...` literal (distinct from the current key); #7782.
const [optIn, setOptIn] = useLocalStorage(
"loopover_notification_opt_in",
false,
- "loopover_notification_opt_in",
+ "gittensory_notification_opt_in",
);
const [busy, setBusy] = useState(false);
diff --git a/scripts/branding-drift-baseline.json b/scripts/branding-drift-baseline.json
index b161c01de0..704b782d0c 100644
--- a/scripts/branding-drift-baseline.json
+++ b/scripts/branding-drift-baseline.json
@@ -1,5 +1,7 @@
{
"apps/loopover-ui/src/components/site/api/try-it.tsx": 1,
+ "apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx": 2,
+ "apps/loopover-ui/src/components/site/notification-readiness-card.tsx": 2,
"apps/loopover-ui/src/routes/app.index.tsx": 1,
"apps/loopover-ui/src/routes/app.runs.tsx": 1,
"apps/loopover-ui/src/routes/app.workbench.tsx": 1,