From 3eb6032f96adcccad5e28a06d2c801daf247e1d4 Mon Sep 17 00:00:00 2001
From: shin-core <153108882+shin-core@users.noreply.github.com>
Date: Wed, 22 Jul 2026 01:30:03 +0900
Subject: [PATCH] fix(ui): restore the pre-rebrand gittensory_ legacy
localStorage keys
#5743's blanket gittensory->loopover rename overwrote two localStorage legacy-key
string literals that were supposed to keep the OLD prefix, so useLocalStorage's
migration fallback read the same key it had just missed under -- silently dropping
a pre-rebrand preference. Restore LEGACY_DISMISS_KEY in onboarding-preview-card and
the notification opt-in legacy key in notification-readiness-card to their
gittensory_-prefixed values (matching the still-correct try-it.tsx pattern), so a
maintainer's pre-rebrand onboarding dismissal / notification opt-in migrates
forward instead of appearing lost. Regenerate scripts/branding-drift-baseline.json
so the two intentionally-restored gittensory_ literals are recorded, not flagged as
drift. Adds a regression test per component that seeds the legacy key and asserts
the value is read forward to the current key.
---
.../onboarding-preview-card.test.tsx | 24 +++++++++++++++++++
.../app-panels/onboarding-preview-card.tsx | 6 +++--
.../site/notification-readiness-card.test.tsx | 23 ++++++++++++++++++
.../site/notification-readiness-card.tsx | 4 +++-
scripts/branding-drift-baseline.json | 2 ++
5 files changed, 56 insertions(+), 3 deletions(-)
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..40c533c4b8 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
@@ -128,6 +128,30 @@ describe("OnboardingPreviewCard", () => {
expect(screen.getByText("503 Service Unavailable")).toBeTruthy();
});
+ it("migrates a pre-rebrand dismissal stored under the legacy gittensory_ key (#7782)", async () => {
+ // A maintainer who dismissed this card before the rebrand has the flag under the OLD gittensory_-prefixed
+ // key. #5743's blanket rename had corrupted the legacyKey to equal the current key, so this value was
+ // silently dropped and the card reappeared. With the legacy key restored, the pre-rebrand dismissal is read
+ // forward: the card stays hidden and no demo API call runs.
+ window.localStorage.clear();
+ // useLocalStorage stores the `{ dismissed }` object shape, so the legacy value mirrors it.
+ 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 }),
+ ),
+ );
+ // The migrated legacy dismissal keeps the card hidden and skips the demo API call entirely.
+ expect(screen.queryByText(/Here's what LoopOver would have flagged/)).toBeNull();
+ expect(apiFetch).not.toHaveBeenCalled();
+ });
+
it("dismisses the card and keeps it hidden (and skips the API call) across a remount", async () => {
apiFetch.mockResolvedValue({ ok: true, data: preview() });
const { unmount } = render();
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..e02902ae67 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
@@ -18,8 +18,10 @@ import { useLocalStorage } from "@/lib/use-local-storage";
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";
+// One-time rebrand migration fallback -- see useLocalStorage's legacyKey param. This must stay the OLD
+// gittensory_-prefixed literal; #5743's blanket gittensory->loopover rename corrupted it to equal DISMISS_KEY,
+// silently dropping a pre-rebrand dismissal (#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..f971ace191 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
@@ -94,4 +94,27 @@ 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 opt-in stored under the legacy gittensory_ key (#7782)", () => {
+ // A maintainer who opted in before the rebrand has their preference under the OLD gittensory_-prefixed
+ // key. #5743's blanket rename had corrupted the legacyKey to equal the current key, so this value was
+ // silently dropped and the card defaulted back to "opt-in required". With the legacy key restored, the
+ // pre-rebrand opt-in is read forward and the pill shows "opt-in enabled".
+ window.localStorage.clear();
+ window.localStorage.setItem("gittensory_notification_opt_in", JSON.stringify(true));
+ useApiResource.mockReturnValue({
+ status: "ready",
+ data: notificationModelFixture,
+ error: null,
+ loadedAt: Date.now(),
+ reload: () => {},
+ });
+
+ render();
+
+ expect(screen.getByText("opt-in enabled")).toBeTruthy();
+ // The legacy value was written forward to the current key (useLocalStorage's migration contract).
+ expect(window.localStorage.getItem("loopover_notification_opt_in")).toBe(JSON.stringify(true));
+ window.localStorage.clear();
+ });
});
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..552f9f5310 100644
--- a/apps/loopover-ui/src/components/site/notification-readiness-card.tsx
+++ b/apps/loopover-ui/src/components/site/notification-readiness-card.tsx
@@ -33,7 +33,9 @@ export function NotificationReadinessCard() {
const [optIn, setOptIn] = useLocalStorage(
"loopover_notification_opt_in",
false,
- "loopover_notification_opt_in",
+ // Legacy fallback must stay the OLD gittensory_-prefixed literal; #5743's blanket gittensory->loopover
+ // rename corrupted it to equal the current key, silently dropping a pre-rebrand opt-in (#7782).
+ "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,