diff --git a/src/components/onboarding/__tests__/confirm-screen.test.tsx b/src/components/onboarding/__tests__/confirm-screen.test.tsx
index bef1dc2ae..274a05b74 100644
--- a/src/components/onboarding/__tests__/confirm-screen.test.tsx
+++ b/src/components/onboarding/__tests__/confirm-screen.test.tsx
@@ -1,6 +1,12 @@
/**
* v1.39 (Wave C) — what the confirm screen is allowed to claim.
*
+ * C5 (research M-l): both of the screen's own Settings links are page routes,
+ * and `hl_onboarding=pending` is still set while this screen is on — the
+ * completion is what clears it, and this screen is what calls the completion.
+ * The proxy sends both straight back to the flow, so on a first run they are
+ * dead links under a sentence that is not yet true.
+ *
* C4 (research I6): the screen listed what the answers switch ON and said
* everything else "stays one click away under Settings, Modules" — true of
* reachability, false of the navigation entry. A module the answers do not
@@ -84,3 +90,20 @@ describe(" — modules leaving the navigation (C4)", () => {
expect(html).not.toContain('data-slot="onboarding-confirm-modules-off"');
});
});
+
+describe(" — Settings links on a first run (C5)", () => {
+ it("renders both as plain text while the completion has not run", () => {
+ const html = render(state());
+ expect(html).not.toContain('href="/settings/modules"');
+ expect(html).not.toContain('href="/settings/account"');
+ // The words stay: they say where the thing lives, which is true.
+ expect(html).toContain("Settings, Modules");
+ expect(html).toContain("Settings, Account");
+ });
+
+ it("renders them as links once the flow has been completed once", () => {
+ const html = render(state({ completedAt: "2026-09-10T08:00:00.000Z" }), {});
+ expect(html).toContain('href="/settings/modules"');
+ expect(html).toContain('href="/settings/account"');
+ });
+});
diff --git a/src/components/onboarding/confirm-screen.tsx b/src/components/onboarding/confirm-screen.tsx
index afee75e5d..dae55b0c8 100644
--- a/src/components/onboarding/confirm-screen.tsx
+++ b/src/components/onboarding/confirm-screen.tsx
@@ -27,6 +27,7 @@ import {
/** A step the confirm screen may pass on the ledger without an answer. */
type PassableStepId = (typeof ONBOARDING_SKIPPABLE_STEP_IDS)[number];
+import { isOnboardingSettled } from "@/lib/onboarding/needs";
import {
firstResultApplies,
nextScreen,
@@ -111,6 +112,17 @@ export function ConfirmScreen({ state }: { state: OnboardingStateDto }) {
const ledger = useRef(state);
const needs = state.needs;
+ /**
+ * v1.39 (Wave C, C5) — is `hl_onboarding=pending` still set? (research M-l)
+ *
+ * Both of this screen's Settings links are page routes, and the cookie is
+ * cleared by the completion — which is what THIS screen calls. So on a
+ * first run the proxy 307s both straight back into the flow: two links
+ * that cannot go anywhere, under a sentence saying where things live. The
+ * words are true, the links are not, so only the links go.
+ */
+ const settingsReachable =
+ isOnboardingSettled(state) || state.completedAt !== null;
const forSomeoneElse = needs.recordTarget === "someone-else";
// v1.39 (Wave C, C4) — `user.modules` is the map the navigation reads, so
// the screen can only name a module the person actually has today.
@@ -201,12 +213,18 @@ export function ConfirmScreen({ state }: { state: OnboardingStateDto }) {