Unify the unlock redirect onto one builder #150
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lz/agent-nexus#150
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Four sites decide where the operator lands after unlocking, and they disagree. OAuth consent arrives as a cold navigation, which is exactly the path that loses the destination — so this is a prerequisite for MCP. It is worth doing on its own merits regardless.
Verified against the live locked instance
The bounce drops the entire destination, not just the query string. Navigate to
/settings?tab=tokenswhile locked and after unlocking you land on/.The four sites
lib/api/unlock-redirect.ts:50lib/components/Unlock.svelte:23sanitizeNextroutes/+layout.server.ts:21redirect(303, required)lib/components/Header.svelte:35goto('/unlock')after LockPlus a fifth leak in the other direction:
+layout.server.ts:25bounces off the lock screens withredirect(303, '/'), discarding anynextalready present.The complete implementation is the client 401 path; the incomplete one runs on a cold navigation.
Once the server bounce learns
next, sites 3 and 4 actively fight.lock()callsinvalidateAll(), the layout re-runs and bounces to/unlock?next=…, and then the explicitgoto('/unlock')overwrites it with a bare one. The duplication stops being a smell and becomes a bug.Work
One builder in
lib/api/unlock-redirect.ts— already outside$lib/server, so both sides run one implementation, the same deliberate choice aslib/git-ref.ts:All four callers route through it.
+layout.server.ts:25honourssanitizeNext(url.searchParams.get('next'))instead of hardcoding/.NO_REDIRECT_FROMmoves into the builder, so no caller can emitnext=/unlockand loop — subsuming the layout's separateurl.pathname !== requiredguard rather than sitting beside it.Decisions
nextis carried for the unlock screen only, not for everyPHASE_ROUTEbounce.needs-setuphands off toneeds-unlockimmediately, so anextthreaded through/setupwould be dropped at the next hop — machinery that looks like it works and does not.Deliberate small UX change: the Lock button now returns you where you were after unlocking. Making it the exception would be a second behaviour to remember.
Done when
A cold navigation to any page while locked returns you to that page after unlocking, query string intact — and a meta-test fails if a
'/unlock'literal appears outsideunlock-redirect.tsandphase-routes.ts. That test is the guard against a fifth path appearing, so prove it fails by adding one.