Skip to content

Hosting: land the new owner signed in with the settings panel open - #1441

Merged
feruzm merged 4 commits into
developfrom
feature/hosting-setup-handoff
Aug 12, 2026
Merged

Hosting: land the new owner signed in with the settings panel open#1441
feruzm merged 4 commits into
developfrom
feature/hosting-setup-handoff

Conversation

@feruzm

@feruzm feruzm commented Aug 11, 2026

Copy link
Copy Markdown
Member

Closes #1417. Stacked on #1439 (which stacks on #1438); base retargets as those merge.

The signup success screen ended with a static list telling the owner to visit their new site, sign in again and find the settings button. Now:

  • The success screen carries a primary "Customize your blog" action deep-linking to the instance with ?setup=1, plus ?login=hivesigner when the ecency.com session is a Hivesigner one.
  • Security model: the login param only STARTS the instance's own OAuth flow. The state nonce is issued by the instance and verified at /auth exactly like a manual login, so identity is never accepted from URL parameters (the exact bug class the dedicated /auth route was built to prevent). No token ever rides in a URL.
  • The setup intent survives the OAuth round trip in sessionStorage; once the owner is looking at their site, the Configuration Editor opens by itself. The params are stripped before anything else happens, so a refresh cannot replay the handoff.
  • First run as owner (any rail, including the HBD memo-only path that can never see the customize step): a one-time dismissible checklist beside the settings button (pick a style, set an accent, check your title), per account.

Tests: the handoff module (param consumption and URL stripping, redirect only when Hivesigner is offered and nobody is signed in, intent surviving the redirect, per-account first-run gate) and the success screen's link shape for a non-Hivesigner session. SPA 885 tests, apps/web 2623, both typechecks green.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (6)

Grey Divider


Action required

1. consumeSetupHandoff effect missing deps ✓ Resolved 📜 Skill insight ≡ Correctness
Description
AuthProvider runs consumeSetupHandoff() in a useEffect([]) while referencing
canHandoffLogin, and suppresses exhaustive-deps, so the effect can run with stale values and
skip/incorrectly trigger the handoff behavior.
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R61-65]

+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
Relevance

●● Moderate

Team often accepts adding missing hook deps, but this effect is intentionally one-shot; change may
alter behavior.

PR-#690

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668377 requires complete hook dependency arrays. The new useEffect references
canHandoffLogin but is declared with [] and explicitly disables exhaustive-deps, which violates
the rule.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-66]
Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useEffect` in `AuthProvider` captures `canHandoffLogin` but uses an empty dependency array and disables `react-hooks/exhaustive-deps`, which can lead to stale logic and missed/incorrect handoff behavior.
## Issue Context
This PR adds a URL-param handoff (`?setup=1`, `?login=hivesigner`) consumed on load. The current implementation intentionally runs once, but it should still obey hook dependency requirements by either:
- including dependencies and keeping the function idempotent, or
- using a `useRef` guard to ensure the effect body executes only once while still listing dependencies.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[60-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Setup intent missed ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() sets the “setup pending” flag inside AuthProvider’s mount effect, but
FloatingMenu checks isSetupPending() in its own effect and won’t rerun when sessionStorage changes.
This ordering dependency can cause the first handoff load to miss the intent and fail to auto-open
settings (falling back to the normal UI).
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R60-63]

+  const canHandoffLogin = isAuthEnabled && availableMethods.includes('hivesigner') && !user;
+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
Relevance

●● Moderate

Real but subtle effect-order race; no close precedent on fixing parent/child useEffect ordering.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
AuthProvider consumes the handoff only in a mount-only effect, while FloatingMenu checks for the
sessionStorage flag in its own effect keyed only on account, and will not rerun when the flag is
written. Because FloatingMenu is rendered under AuthProvider, the two mount effects can execute in
an order that allows FloatingMenu to read before AuthProvider writes, leaving the intent unseen for
that load.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
apps/self-hosted/src/features/auth/setup-handoff.ts[36-60]
apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
apps/self-hosted/src/routes/__root.tsx[96-100]
🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The handoff intent (`ecency:setup-pending`) is currently created inside `AuthProvider`’s `useEffect`. `FloatingMenu` reads that intent inside its own `useEffect([account])` and does not rerun when sessionStorage changes, so it can miss the intent on the initial handoff page load.
## Issue Context
This is an ordering/synchronization dependency between two separate mount effects. If `FloatingMenu` runs its effect before `AuthProvider` writes the flag, the settings panel won’t auto-open as intended.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
- apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
- apps/self-hosted/src/index.tsx[115-137]
## Implementation guidance
Prefer making the handoff consumption happen before React mounts consumers:
1. Move (or duplicate) the `consumeSetupHandoff(...)` call into `apps/self-hosted/src/index.tsx` inside `main()` after `await InstanceConfigManager.initialize()` and before `root.render(...)`.
2. Compute `canLoginWithHivesigner` there using config + stored user (e.g., `getUser()` from auth storage) so behavior matches current gating.
3. Keep the AuthProvider effect only if you want defense-in-depth, but it should become a no-op because the URL params will already be stripped.
Alternative approach: lift the pending intent into a shared store/event so `FloatingMenu` can react when the intent is set (but the pre-mount consumption is simplest and matches the “strip before anything else happens” goal).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. consumeSetupHandoff effect missing deps ✓ Resolved 📜 Skill insight ≡ Correctness
Description
AuthProvider runs consumeSetupHandoff() in a useEffect([]) while referencing
canHandoffLogin, and suppresses exhaustive-deps, so the effect can run with stale values and
skip/incorrectly trigger the handoff behavior.
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R61-65]

+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
Relevance

●● Moderate

Team often accepts adding missing hook deps, but this effect is intentionally one-shot; change may
alter behavior.

PR-#690

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668377 requires complete hook dependency arrays. The new useEffect references
canHandoffLogin but is declared with [] and explicitly disables exhaustive-deps, which violates
the rule.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-66]
Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useEffect` in `AuthProvider` captures `canHandoffLogin` but uses an empty dependency array and disables `react-hooks/exhaustive-deps`, which can lead to stale logic and missed/incorrect handoff behavior.
## Issue Context
This PR adds a URL-param handoff (`?setup=1`, `?login=hivesigner`) consumed on load. The current implementation intentionally runs once, but it should still obey hook dependency requirements by either:
- including dependencies and keeping the function idempotent, or
- using a `useRef` guard to ensure the effect body executes only once while still listing dependencies.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[60-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View action required (7)
4. Setup intent missed ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() sets the “setup pending” flag inside AuthProvider’s mount effect, but
FloatingMenu checks isSetupPending() in its own effect and won’t rerun when sessionStorage changes.
This ordering dependency can cause the first handoff load to miss the intent and fail to auto-open
settings (falling back to the normal UI).
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R60-63]

+  const canHandoffLogin = isAuthEnabled && availableMethods.includes('hivesigner') && !user;
+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
Relevance

●● Moderate

Real but subtle effect-order race; no close precedent on fixing parent/child useEffect ordering.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
AuthProvider consumes the handoff only in a mount-only effect, while FloatingMenu checks for the
sessionStorage flag in its own effect keyed only on account, and will not rerun when the flag is
written. Because FloatingMenu is rendered under AuthProvider, the two mount effects can execute in
an order that allows FloatingMenu to read before AuthProvider writes, leaving the intent unseen for
that load.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
apps/self-hosted/src/features/auth/setup-handoff.ts[36-60]
apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
apps/self-hosted/src/routes/__root.tsx[96-100]
🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The handoff intent (`ecency:setup-pending`) is currently created inside `AuthProvider`’s `useEffect`. `FloatingMenu` reads that intent inside its own `useEffect([account])` and does not rerun when sessionStorage changes, so it can miss the intent on the initial handoff page load.
## Issue Context
This is an ordering/synchronization dependency between two separate mount effects. If `FloatingMenu` runs its effect before `AuthProvider` writes the flag, the settings panel won’t auto-open as intended.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
- apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
- apps/self-hosted/src/index.tsx[115-137]
## Implementation guidance
Prefer making the handoff consumption happen before React mounts consumers:
1. Move (or duplicate) the `consumeSetupHandoff(...)` call into `apps/self-hosted/src/index.tsx` inside `main()` after `await InstanceConfigManager.initialize()` and before `root.render(...)`.
2. Compute `canLoginWithHivesigner` there using config + stored user (e.g., `getUser()` from auth storage) so behavior matches current gating.
3. Keep the AuthProvider effect only if you want defense-in-depth, but it should become a no-op because the URL params will already be stripped.
Alternative approach: lift the pending intent into a shared store/event so `FloatingMenu` can react when the intent is set (but the pre-mount consumption is simplest and matches the “strip before anything else happens” goal).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. consumeSetupHandoff effect missing deps ✓ Resolved 📜 Skill insight ≡ Correctness
Description
AuthProvider runs consumeSetupHandoff() in a useEffect([]) while referencing
canHandoffLogin, and suppresses exhaustive-deps, so the effect can run with stale values and
skip/incorrectly trigger the handoff behavior.
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R61-65]

+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
Relevance

●● Moderate

Team often accepts adding missing hook deps, but this effect is intentionally one-shot; change may
alter behavior.

PR-#690

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668377 requires complete hook dependency arrays. The new useEffect references
canHandoffLogin but is declared with [] and explicitly disables exhaustive-deps, which violates
the rule.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-66]
Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useEffect` in `AuthProvider` captures `canHandoffLogin` but uses an empty dependency array and disables `react-hooks/exhaustive-deps`, which can lead to stale logic and missed/incorrect handoff behavior.
## Issue Context
This PR adds a URL-param handoff (`?setup=1`, `?login=hivesigner`) consumed on load. The current implementation intentionally runs once, but it should still obey hook dependency requirements by either:
- including dependencies and keeping the function idempotent, or
- using a `useRef` guard to ensure the effect body executes only once while still listing dependencies.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[60-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Setup intent missed ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() sets the “setup pending” flag inside AuthProvider’s mount effect, but
FloatingMenu checks isSetupPending() in its own effect and won’t rerun when sessionStorage changes.
This ordering dependency can cause the first handoff load to miss the intent and fail to auto-open
settings (falling back to the normal UI).
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R60-63]

+  const canHandoffLogin = isAuthEnabled && availableMethods.includes('hivesigner') && !user;
+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
Relevance

●● Moderate

Real but subtle effect-order race; no close precedent on fixing parent/child useEffect ordering.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
AuthProvider consumes the handoff only in a mount-only effect, while FloatingMenu checks for the
sessionStorage flag in its own effect keyed only on account, and will not rerun when the flag is
written. Because FloatingMenu is rendered under AuthProvider, the two mount effects can execute in
an order that allows FloatingMenu to read before AuthProvider writes, leaving the intent unseen for
that load.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
apps/self-hosted/src/features/auth/setup-handoff.ts[36-60]
apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
apps/self-hosted/src/routes/__root.tsx[96-100]
🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The handoff intent (`ecency:setup-pending`) is currently created inside `AuthProvider`’s `useEffect`. `FloatingMenu` reads that intent inside its own `useEffect([account])` and does not rerun when sessionStorage changes, so it can miss the intent on the initial handoff page load.
## Issue Context
This is an ordering/synchronization dependency between two separate mount effects. If `FloatingMenu` runs its effect before `AuthProvider` writes the flag, the settings panel won’t auto-open as intended.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
- apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
- apps/self-hosted/src/index.tsx[115-137]
## Implementation guidance
Prefer making the handoff consumption happen before React mounts consumers:
1. Move (or duplicate) the `consumeSetupHandoff(...)` call into `apps/self-hosted/src/index.tsx` inside `main()` after `await InstanceConfigManager.initialize()` and before `root.render(...)`.
2. Compute `canLoginWithHivesigner` there using config + stored user (e.g., `getUser()` from auth storage) so behavior matches current gating.
3. Keep the AuthProvider effect only if you want defense-in-depth, but it should become a no-op because the URL params will already be stripped.
Alternative approach: lift the pending intent into a shared store/event so `FloatingMenu` can react when the intent is set (but the pre-mount consumption is simplest and matches the “strip before anything else happens” goal).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. consumeSetupHandoff effect missing deps ✓ Resolved 📜 Skill insight ≡ Correctness
Description
AuthProvider runs consumeSetupHandoff() in a useEffect([]) while referencing
canHandoffLogin, and suppresses exhaustive-deps, so the effect can run with stale values and
skip/incorrectly trigger the handoff behavior.
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R61-65]

+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
Relevance

●● Moderate

Team often accepts adding missing hook deps, but this effect is intentionally one-shot; change may
alter behavior.

PR-#690

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668377 requires complete hook dependency arrays. The new useEffect references
canHandoffLogin but is declared with [] and explicitly disables exhaustive-deps, which violates
the rule.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-66]
Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useEffect` in `AuthProvider` captures `canHandoffLogin` but uses an empty dependency array and disables `react-hooks/exhaustive-deps`, which can lead to stale logic and missed/incorrect handoff behavior.
## Issue Context
This PR adds a URL-param handoff (`?setup=1`, `?login=hivesigner`) consumed on load. The current implementation intentionally runs once, but it should still obey hook dependency requirements by either:
- including dependencies and keeping the function idempotent, or
- using a `useRef` guard to ensure the effect body executes only once while still listing dependencies.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[60-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Setup intent missed ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() sets the “setup pending” flag inside AuthProvider’s mount effect, but
FloatingMenu checks isSetupPending() in its own effect and won’t rerun when sessionStorage changes.
This ordering dependency can cause the first handoff load to miss the intent and fail to auto-open
settings (falling back to the normal UI).
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R60-63]

+  const canHandoffLogin = isAuthEnabled && availableMethods.includes('hivesigner') && !user;
+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
Relevance

●● Moderate

Real but subtle effect-order race; no close precedent on fixing parent/child useEffect ordering.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
AuthProvider consumes the handoff only in a mount-only effect, while FloatingMenu checks for the
sessionStorage flag in its own effect keyed only on account, and will not rerun when the flag is
written. Because FloatingMenu is rendered under AuthProvider, the two mount effects can execute in
an order that allows FloatingMenu to read before AuthProvider writes, leaving the intent unseen for
that load.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
apps/self-hosted/src/features/auth/setup-handoff.ts[36-60]
apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
apps/self-hosted/src/routes/__root.tsx[96-100]
🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The handoff intent (`ecency:setup-pending`) is currently created inside `AuthProvider`’s `useEffect`. `FloatingMenu` reads that intent inside its own `useEffect([account])` and does not rerun when sessionStorage changes, so it can miss the intent on the initial handoff page load.
## Issue Context
This is an ordering/synchronization dependency between two separate mount effects. If `FloatingMenu` runs its effect before `AuthProvider` writes the flag, the settings panel won’t auto-open as intended.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
- apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
- apps/self-hosted/src/index.tsx[115-137]
## Implementation guidance
Prefer making the handoff consumption happen before React mounts consumers:
1. Move (or duplicate) the `consumeSetupHandoff(...)` call into `apps/self-hosted/src/index.tsx` inside `main()` after `await InstanceConfigManager.initialize()` and before `root.render(...)`.
2. Compute `canLoginWithHivesigner` there using config + stored user (e.g., `getUser()` from auth storage) so behavior matches current gating.
3. Keep the AuthProvider effect only if you want defense-in-depth, but it should become a no-op because the URL params will already be stripped.
Alternative approach: lift the pending intent into a shared store/event so `FloatingMenu` can react when the intent is set (but the pre-mount consumption is simplest and matches the “strip before anything else happens” goal).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. consumeSetupHandoff effect missing deps ✓ Resolved 📜 Skill insight ≡ Correctness
Description
AuthProvider runs consumeSetupHandoff() in a useEffect([]) while referencing
canHandoffLogin, and suppresses exhaustive-deps, so the effect can run with stale values and
skip/incorrectly trigger the handoff behavior.
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R61-65]

+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
Relevance

●● Moderate

Team often accepts adding missing hook deps, but this effect is intentionally one-shot; change may
alter behavior.

PR-#690

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668377 requires complete hook dependency arrays. The new useEffect references
canHandoffLogin but is declared with [] and explicitly disables exhaustive-deps, which violates
the rule.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-66]
Skill: code-review: Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useEffect` in `AuthProvider` captures `canHandoffLogin` but uses an empty dependency array and disables `react-hooks/exhaustive-deps`, which can lead to stale logic and missed/incorrect handoff behavior.
## Issue Context
This PR adds a URL-param handoff (`?setup=1`, `?login=hivesigner`) consumed on load. The current implementation intentionally runs once, but it should still obey hook dependency requirements by either:
- including dependencies and keeping the function idempotent, or
- using a `useRef` guard to ensure the effect body executes only once while still listing dependencies.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[60-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Setup intent missed ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() sets the “setup pending” flag inside AuthProvider’s mount effect, but
FloatingMenu checks isSetupPending() in its own effect and won’t rerun when sessionStorage changes.
This ordering dependency can cause the first handoff load to miss the intent and fail to auto-open
settings (falling back to the normal UI).
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R60-63]

+  const canHandoffLogin = isAuthEnabled && availableMethods.includes('hivesigner') && !user;
+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
Relevance

●● Moderate

Real but subtle effect-order race; no close precedent on fixing parent/child useEffect ordering.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
AuthProvider consumes the handoff only in a mount-only effect, while FloatingMenu checks for the
sessionStorage flag in its own effect keyed only on account, and will not rerun when the flag is
written. Because FloatingMenu is rendered under AuthProvider, the two mount effects can execute in
an order that allows FloatingMenu to read before AuthProvider writes, leaving the intent unseen for
that load.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
apps/self-hosted/src/features/auth/setup-handoff.ts[36-60]
apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
apps/self-hosted/src/routes/__root.tsx[96-100]
🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.: 🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The handoff intent (`ecency:setup-pending`) is currently created inside `AuthProvider`’s `useEffect`. `FloatingMenu` reads that intent inside its own `useEffect([account])` and does not rerun when sessionStorage changes, so it can miss the intent on the initial handoff page load.
## Issue Context
This is an ordering/synchronization dependency between two separate mount effects. If `FloatingMenu` runs its effect before `AuthProvider` writes the flag, the settings panel won’t auto-open as intended.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
- apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
- apps/self-hosted/src/index.tsx[115-137]
## Implementation guidance
Prefer making the handoff consumption happen before React mounts consumers:
1. Move (or duplicate) the `consumeSetupHandoff(...)` call into `apps/self-hosted/src/index.tsx` inside `main()` after `await InstanceConfigManager.initialize()` and before `root.render(...)`.
2. Compute `canLoginWithHivesigner` there using config + stored user (e.g., `getUser()` from auth storage) so behavior matches current gating.
3. Keep the AuthProvider effect only if you want defense-in-depth, but it should become a no-op because the URL params will already be stripped.
Alternative approach: lift the pending intent into a shared store/event so `FloatingMenu` can react when the intent is set (but the pre-mount consumption is simplest and matches the “strip before anything else happens” goal).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

11. Hash fragment stripped ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() rebuilds the URL using only pathname + remaining query params, dropping
window.location.hash. Any handoff link containing a fragment (e.g. /page?setup=1#section) will lose
the fragment after consumption.
Code

apps/self-hosted/src/features/auth/setup-handoff.ts[R46-50]

+  window.history.replaceState(
+    null,
+    '',
+    window.location.pathname + (qs ? `?${qs}` : ''),
+  );
Relevance

●●● Strong

Low-risk correctness fix; repo already uses replaceState for URL cleanup, so preserving hash is
consistent.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The replaceState call reconstructs the URL from pathname and the remaining query string only; it
never appends window.location.hash, so any fragment present is removed.

apps/self-hosted/src/features/auth/setup-handoff.ts[38-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When consuming `?setup=1` / `?login=hivesigner`, the code uses `history.replaceState` to remove those query params, but it does not preserve `window.location.hash`. This unintentionally removes any existing fragment.
## Issue Context
The function intentionally preserves unrelated query parameters; dropping the fragment is inconsistent and can break anchor navigation or hash-based routing on handoff URLs.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/setup-handoff.ts[43-50]
## Implementation guidance
Include the current hash when rebuilding the URL:
- Append `window.location.hash` to the replacement URL (after the query string), or
- Construct via `const url = new URL(window.location.href); url.searchParams.delete(...); window.history.replaceState(null, '', url.pathname + url.search + url.hash);`
Ensure tests cover a URL like `/?setup=1#x` preserving `#x` after consumption.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Hash fragment stripped ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() rebuilds the URL using only pathname + remaining query params, dropping
window.location.hash. Any handoff link containing a fragment (e.g. /page?setup=1#section) will lose
the fragment after consumption.
Code

apps/self-hosted/src/features/auth/setup-handoff.ts[R46-50]

+  window.history.replaceState(
+    null,
+    '',
+    window.location.pathname + (qs ? `?${qs}` : ''),
+  );
Relevance

●●● Strong

Low-risk correctness fix; repo already uses replaceState for URL cleanup, so preserving hash is
consistent.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The replaceState call reconstructs the URL from pathname and the remaining query string only; it
never appends window.location.hash, so any fragment present is removed.

apps/self-hosted/src/features/auth/setup-handoff.ts[38-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When consuming `?setup=1` / `?login=hivesigner`, the code uses `history.replaceState` to remove those query params, but it does not preserve `window.location.hash`. This unintentionally removes any existing fragment.
## Issue Context
The function intentionally preserves unrelated query parameters; dropping the fragment is inconsistent and can break anchor navigation or hash-based routing on handoff URLs.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/setup-handoff.ts[43-50]
## Implementation guidance
Include the current hash when rebuilding the URL:
- Append `window.location.hash` to the replacement URL (after the query string), or
- Construct via `const url = new URL(window.location.href); url.searchParams.delete(...); window.history.replaceState(null, '', url.pathname + url.search + url.hash);`
Ensure tests cover a URL like `/?setup=1#x` preserving `#x` after consumption.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. Hash fragment stripped ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() rebuilds the URL using only pathname + remaining query params, dropping
window.location.hash. Any handoff link containing a fragment (e.g. /page?setup=1#section) will lose
the fragment after consumption.
Code

apps/self-hosted/src/features/auth/setup-handoff.ts[R46-50]

+  window.history.replaceState(
+    null,
+    '',
+    window.location.pathname + (qs ? `?${qs}` : ''),
+  );
Relevance

●●● Strong

Low-risk correctness fix; repo already uses replaceState for URL cleanup, so preserving hash is
consistent.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The replaceState call reconstructs the URL from pathname and the remaining query string only; it
never appends window.location.hash, so any fragment present is removed.

apps/self-hosted/src/features/auth/setup-handoff.ts[38-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When consuming `?setup=1` / `?login=hivesigner`, the code uses `history.replaceState` to remove those query params, but it does not preserve `window.location.hash`. This unintentionally removes any existing fragment.
## Issue Context
The function intentionally preserves unrelated query parameters; dropping the fragment is inconsistent and can break anchor navigation or hash-based routing on handoff URLs.
## Fix Focus Areas
- apps/self-hosted/src/features/auth/setup-handoff.ts[43-50]
## Implementation guidance
Include the current hash when rebuilding the URL:
- Append `win...

@qodo-code-review

qodo-code-review Bot commented Aug 11, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Hosting: deep-link new owners into settings with secure login handoff

✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add a “Customize your blog” success CTA that deep-links to the new instance with ?setup=1 (and
 optional ?login=hivesigner).
• Consume and strip handoff params on the instance, persisting setup intent across OAuth safely.
• Auto-open the owner settings panel and show a one-time per-account first-run checklist.
Diagram

graph TD
  A["apps/web: Signup success"] --> B["Link to instance\n?setup=1 (& login=hivesigner)"] --> C["Instance AuthProvider"] --> D["consumeSetupHandoff()"] --> E[("Web Storage\nsession/local")] --> F["Owner FloatingMenu"] --> G["Settings panel opens"]
  D --> H{"login=hivesigner\n& allowed & signed out?"} --> I["/auth OAuth redirect"] --> C
  subgraph Legend
    direction LR
    _cmp["Component"] ~~~ _dec{"Decision"} ~~~ _store[("Storage")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Dedicated /setup route instead of query params
  • ➕ Keeps intent out of the main app route and can centralize param stripping/redirect logic
  • ➕ Easier to document/share as a single stable entrypoint
  • ➖ Still needs storage to survive OAuth round-trip
  • ➖ Requires extra routing and may complicate deep links that also include existing filters/search params
2. Signed, server-minted handoff token (JWT)
  • ➕ Could encode richer state without relying on web storage
  • ➕ Can enforce tighter expiry and replay protections server-side
  • ➖ More infrastructure and key management
  • ➖ Overkill for a simple setup intent (no auth credentials are being carried)

Recommendation: Current approach is appropriate: it preserves the security boundary by never accepting identity from URL parameters (only triggering the instance’s normal OAuth flow), strips params immediately to prevent replay, and uses minimal client-side state (sessionStorage/localStorage) to bridge the OAuth redirect and gate first-run UX. A dedicated /setup route is a reasonable future refinement if handoff complexity grows, but isn’t necessary for this scope.

Files changed (8) +267 / -1

Enhancement (6) +200 / -1
i18n-strings.tsAdd first-run checklist i18n keys and English strings +12/-0

Add first-run checklist i18n keys and English strings

• Introduces translation keys and English copy for a new owner first-run checklist (title, three items, open/dismiss actions).

apps/self-hosted/src/core/i18n-strings.ts

auth-provider.tsxConsume setup/login handoff params once at auth boot +12/-0

Consume setup/login handoff params once at auth boot

• Wires the new setup handoff consumer into AuthProvider, gated to only start Hivesigner login when auth is enabled, Hivesigner is offered, and the user is signed out. Ensures query params are consumed only once per page load.

apps/self-hosted/src/features/auth/auth-provider.tsx

setup-handoff.tsImplement setup handoff consumption, storage, and first-run gating +90/-0

Implement setup handoff consumption, storage, and first-run gating

• Adds a small module that parses '?setup=1' and '?login=hivesigner', strips both from the URL to prevent replay, stores setup intent in sessionStorage, and tracks per-account first-run dismissal in localStorage. Optionally triggers the instance’s normal Hivesigner OAuth redirect when allowed.

apps/self-hosted/src/features/auth/setup-handoff.ts

floating-menu.tsxAuto-open settings on setup intent and show first-run checklist +66/-1

Auto-open settings on setup intent and show first-run checklist

• Reads the setup-pending intent to open the owner floating settings menu automatically and clears the intent after use. Adds a one-time, per-account dismissible checklist UI that points owners to the settings button when no setup intent is present.

apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx

hosting-signup.tsxAdd “Customize your blog” CTA deep-link with setup/login params +19/-0

Add “Customize your blog” CTA deep-link with setup/login params

• On the hosting success step, adds a primary CTA that opens the new instance in a new tab with '?setup=1'. Appends '?login=hivesigner' when the current ecency.com session uses Hivesigner, enabling a seamless instance-side OAuth start.

apps/web/src/features/hosting-signup/hosting-signup.tsx

en-US.jsonAdd English string for “Customize your blog” +1/-0

Add English string for “Customize your blog”

• Introduces the 'hosting.customize-your-blog' locale entry used by the new success CTA.

apps/web/src/features/i18n/locales/en-US.json

Tests (2) +67 / -0
setup-handoff.test.tsAdd jsdom tests for handoff consumption and first-run persistence +62/-0

Add jsdom tests for handoff consumption and first-run persistence

• Covers URL param consumption/stripping behavior, conditional Hivesigner redirect triggering, setup intent persistence across redirect, and per-account first-run seen tracking.

apps/self-hosted/src/features/auth/setup-handoff.test.ts

hosting-signup.spec.tsxAssert success CTA link shape for non-Hivesigner sessions +5/-0

Assert success CTA link shape for non-Hivesigner sessions

• Extends the hosting signup success test to verify the deep link includes '?setup=1' and does not include a login hint for keychain sessions.

apps/web/src/specs/features/hosting-signup/hosting-signup.spec.tsx

@qodo-code-review

qodo-code-review Bot commented Aug 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (2)

Grey Divider


Action required

1. consumeSetupHandoff effect missing deps ✓ Resolved 📜 Skill insight ≡ Correctness
Description
AuthProvider runs consumeSetupHandoff() in a useEffect([]) while referencing
canHandoffLogin, and suppresses exhaustive-deps, so the effect can run with stale values and
skip/incorrectly trigger the handoff behavior.
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R61-65]

+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
Relevance

●● Moderate

Team often accepts adding missing hook deps, but this effect is intentionally one-shot; change may
alter behavior.

PR-#690

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668377 requires complete hook dependency arrays. The new useEffect references
canHandoffLogin but is declared with [] and explicitly disables exhaustive-deps, which violates
the rule.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-66]
Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useEffect` in `AuthProvider` captures `canHandoffLogin` but uses an empty dependency array and disables `react-hooks/exhaustive-deps`, which can lead to stale logic and missed/incorrect handoff behavior.

## Issue Context
This PR adds a URL-param handoff (`?setup=1`, `?login=hivesigner`) consumed on load. The current implementation intentionally runs once, but it should still obey hook dependency requirements by either:
- including dependencies and keeping the function idempotent, or
- using a `useRef` guard to ensure the effect body executes only once while still listing dependencies.

## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[60-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Setup intent missed ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() sets the “setup pending” flag inside AuthProvider’s mount effect, but
FloatingMenu checks isSetupPending() in its own effect and won’t rerun when sessionStorage changes.
This ordering dependency can cause the first handoff load to miss the intent and fail to auto-open
settings (falling back to the normal UI).
Code

apps/self-hosted/src/features/auth/auth-provider.tsx[R60-63]

+  const canHandoffLogin = isAuthEnabled && availableMethods.includes('hivesigner') && !user;
+  useEffect(() => {
+    consumeSetupHandoff({ canLoginWithHivesigner: canHandoffLogin });
+    // Intentionally once: the params are consumed from the URL on first run.
Relevance

●● Moderate

Real but subtle effect-order race; no close precedent on fixing parent/child useEffect ordering.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
AuthProvider consumes the handoff only in a mount-only effect, while FloatingMenu checks for the
sessionStorage flag in its own effect keyed only on account, and will not rerun when the flag is
written. Because FloatingMenu is rendered under AuthProvider, the two mount effects can execute in
an order that allows FloatingMenu to read before AuthProvider writes, leaving the intent unseen for
that load.

apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
apps/self-hosted/src/features/auth/setup-handoff.ts[36-60]
apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
apps/self-hosted/src/routes/__root.tsx[96-100]
🌐 Discussion notes that React currently flushes useEffect callbacks children-first, which can cause child mount effects to run before parent mount effects in the same commit.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The handoff intent (`ecency:setup-pending`) is currently created inside `AuthProvider`’s `useEffect`. `FloatingMenu` reads that intent inside its own `useEffect([account])` and does not rerun when sessionStorage changes, so it can miss the intent on the initial handoff page load.

## Issue Context
This is an ordering/synchronization dependency between two separate mount effects. If `FloatingMenu` runs its effect before `AuthProvider` writes the flag, the settings panel won’t auto-open as intended.

## Fix Focus Areas
- apps/self-hosted/src/features/auth/auth-provider.tsx[56-65]
- apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx[25-39]
- apps/self-hosted/src/index.tsx[115-137]

## Implementation guidance
Prefer making the handoff consumption happen before React mounts consumers:
1. Move (or duplicate) the `consumeSetupHandoff(...)` call into `apps/self-hosted/src/index.tsx` inside `main()` after `await InstanceConfigManager.initialize()` and before `root.render(...)`.
2. Compute `canLoginWithHivesigner` there using config + stored user (e.g., `getUser()` from auth storage) so behavior matches current gating.
3. Keep the AuthProvider effect only if you want defense-in-depth, but it should become a no-op because the URL params will already be stripped.

Alternative approach: lift the pending intent into a shared store/event so `FloatingMenu` can react when the intent is set (but the pre-mount consumption is simplest and matches the “strip before anything else happens” goal).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Hash fragment stripped ✓ Resolved 🐞 Bug ≡ Correctness
Description
consumeSetupHandoff() rebuilds the URL using only pathname + remaining query params, dropping
window.location.hash. Any handoff link containing a fragment (e.g. /page?setup=1#section) will lose
the fragment after consumption.
Code

apps/self-hosted/src/features/auth/setup-handoff.ts[R46-50]

+  window.history.replaceState(
+    null,
+    '',
+    window.location.pathname + (qs ? `?${qs}` : ''),
+  );
Relevance

●●● Strong

Low-risk correctness fix; repo already uses replaceState for URL cleanup, so preserving hash is
consistent.

PR-#1317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The replaceState call reconstructs the URL from pathname and the remaining query string only; it
never appends window.location.hash, so any fragment present is removed.

apps/self-hosted/src/features/auth/setup-handoff.ts[38-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When consuming `?setup=1` / `?login=hivesigner`, the code uses `history.replaceState` to remove those query params, but it does not preserve `window.location.hash`. This unintentionally removes any existing fragment.

## Issue Context
The function intentionally preserves unrelated query parameters; dropping the fragment is inconsistent and can break anchor navigation or hash-based routing on handoff URLs.

## Fix Focus Areas
- apps/self-hosted/src/features/auth/setup-handoff.ts[43-50]

## Implementation guidance
Include the current hash when rebuilding the URL:
- Append `window.location.hash` to the replacement URL (after the query string), or
- Construct via `const url = new URL(window.location.href); url.searchParams.delete(...); window.history.replaceState(null, '', url.pathname + url.search + url.hash);`
Ensure tests cover a URL like `/?setup=1#x` preserving `#x` after consumption.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Test file uses .test suffix 📜 Skill insight ⚙ Maintainability
Description
The new test file uses the .test.ts naming pattern instead of the required .spec.ts/.spec.tsx
pattern, which breaks the enforced naming convention.
Code

apps/self-hosted/src/features/auth/setup-handoff.test.ts[1]

+// @vitest-environment jsdom
Relevance

● Weak

Exact precedent: team rejected renaming .test.* to .spec.* convention change.

PR-#1437

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668286 requires test files to be named using the .spec.ts/.spec.tsx pattern.
The newly added file is named setup-handoff.test.ts, violating this convention.

apps/self-hosted/src/features/auth/setup-handoff.test.ts[1-1]
Skill: add-test

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test file name uses `.test.ts`, but the compliance standard requires `.spec.ts` (or `.spec.tsx` for React).

## Issue Context
Aligning naming conventions keeps tooling, discovery, and grep patterns consistent across the repo.

## Fix Focus Areas
- apps/self-hosted/src/features/auth/setup-handoff.test.ts[1-1]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Co-located test file added 📜 Skill insight ⌂ Architecture
Description
A new test file is added under src/features/... instead of the required src/specs/... mapping,
violating the repo’s test placement standard and making test organization inconsistent.
Code

apps/self-hosted/src/features/auth/setup-handoff.test.ts[R1-4]

+// @vitest-environment jsdom
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import {
+  clearSetupPending,
Relevance

● Weak

Exact precedent: team rejected moving co-located tests into src/specs mapping.

PR-#1437

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668281 requires tests to be placed under the corresponding src/specs/... subtree
and not co-located with source files. This PR introduces
apps/self-hosted/src/features/auth/setup-handoff.test.ts directly under src/features/....

apps/self-hosted/src/features/auth/setup-handoff.test.ts[1-10]
Skill: add-test

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new test file is co-located with feature code under `apps/self-hosted/src/features/...`, but tests must live under the corresponding `src/specs/...` directory.

## Issue Context
The compliance rule requires tests not be co-located with source files and instead follow the directory mapping convention.

## Fix Focus Areas
- apps/self-hosted/src/features/auth/setup-handoff.test.ts[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Compliance rules (platform): 75 rules
✅ Skills: 6 invoked
  add-feature
  add-query
  add-sdk-mutation
  add-test
  code-review
  debug
✅ Web pages:
  +2 more
Review mode: ⚖️ Balanced: This changes authentication handoff, OAuth initiation, session persistence, URL handling, and owner-facing setup behavior across web and self-hosted paths; it carries genuine security and cross-path risk, but is not so bug-dense across independent logic areas as to require redundant extended review.

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread apps/self-hosted/src/features/auth/auth-provider.tsx Outdated
Comment thread apps/self-hosted/src/features/auth/auth-provider.tsx Outdated
Comment thread apps/self-hosted/src/features/auth/setup-handoff.ts
@feruzm
feruzm force-pushed the feature/hosting-reservation-grace branch from fd0f2ec to ec4829e Compare August 12, 2026 06:02
feruzm added 2 commits August 12, 2026 06:02
The success screen ended with a static list telling the owner to visit
their site, sign in and hunt for the settings button. It now carries a
primary Customize your blog action deep-linking to the instance with a
setup intent, plus ?login=hivesigner when the ecency.com session is a
Hivesigner one.

The login param only STARTS the instance's own OAuth flow: the state
nonce is issued by the instance and verified at /auth exactly like a
manual login, so identity is never accepted from URL parameters. The
setup intent survives the OAuth round trip in sessionStorage; once the
owner is looking at their site, the settings panel opens by itself.

Owners who arrive through rails without the customize step get a
one-time first-run checklist beside the settings button (pick a style,
set an accent, check the title), dismissible and per account.

Closes #1417
Review findings: FloatingMenu checked the setup intent in its own
effect, but child effects run before parent effects, so an already
signed-in owner arriving with ?setup=1 had the flag set AFTER the only
check that would open the panel. Params are now captured at boot in
main(), before React renders anything, so no component's effect order
can decide whether an intent is seen. The provider keeps only the
decision half (actOnLoginRequest) with honest deps, which also retires
the suppressed exhaustive-deps, and the URL rebuild preserves the hash
fragment.
@feruzm
feruzm force-pushed the feature/hosting-setup-handoff branch from e1de899 to 0cd206b Compare August 12, 2026 06:02
Review finding: the intent was consumed on the first decisive run even
when it could not act, so arriving before the instance's client id was
registered silently killed the headline auto-login. It is now retained
while the method is unavailable (and still dropped once signed in), so
a later run or the next load can honor it. The suggested pre-redirect
confirm is deliberately not added: the redirect only reaches the
Hivesigner authorize page, which is itself the user gesture, so a
forged link achieves nothing an ordinary anchor could not.
Base automatically changed from feature/hosting-reservation-grace to develop August 12, 2026 06:09
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@feruzm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3d2c39a5-4897-49e8-9fec-e69e3c5ecfe7

📥 Commits

Reviewing files that changed from the base of the PR and between b088641 and d253c8c.

📒 Files selected for processing (9)
  • apps/self-hosted/src/core/i18n-strings.ts
  • apps/self-hosted/src/features/auth/auth-provider.tsx
  • apps/self-hosted/src/features/auth/setup-handoff.test.ts
  • apps/self-hosted/src/features/auth/setup-handoff.ts
  • apps/self-hosted/src/features/floating-menu/components/floating-menu.tsx
  • apps/self-hosted/src/index.tsx
  • apps/web/src/features/hosting-signup/hosting-signup.tsx
  • apps/web/src/features/i18n/locales/en-US.json
  • apps/web/src/specs/features/hosting-signup/hosting-signup.spec.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@feruzm
feruzm merged commit 5d5fab4 into develop Aug 12, 2026
7 of 8 checks passed
@feruzm
feruzm deleted the feature/hosting-setup-handoff branch August 12, 2026 06:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hosting: land a new owner on their instance signed in, settings open

1 participant