Repository navigation
fix(v2): a phone first login lands in the workspace thread - #1844
Conversation
lilyshen0722
left a comment
There was a problem hiding this comment.
UX-GATE: PASS @ e310850 — TASK-144, gated at 390 + 1440 on a production build of this head served with the 1799 fixture plus an own invite-only workspace pod listed LAST (so the landing proves the ownWorkspace branch, not pods[0]). Measured: (1) 390, LAST_POD_KEY unset → /v2 replaces to /v2/pods/pod-workspace, composer present, key written = pod-workspace; browser back has nothing to step into (replace confirmed at the history level). (2) Same device, /v2 again by full reload and by the thread's Back-to-pods arrow → the pods list (.v2-shell--list), and it stays across a further reload. (3) 390 with the key set (live pod, and a pod no longer listed) → list, no navigate. (4) 1440 unset → /v2/pods/pod-workspace as before. Non-blocking, pre-existing and out of this diff: the first-run hero card now overlays the workspace thread at 390 on first login (composer visible beneath, Skip for now present) where it used to sit on the list — same component, same rendering rule, fine by the ruling; the thread back arrow measures 28×28 at 390 against the 44 phone act rule; header reads '1 agents'. Note the ruling named the jsdom phone mock at (max-width: 760px) — the test's matchMedia stub matches that string exactly, so a query change would go red rather than silently pass.
e310850 to
f196bbd
Compare
lilyshen0722
left a comment
There was a problem hiding this comment.
GATE: PASS at f196bbd2 — measured. BASE 18/18, RESTORED 18/18, tree clean after every mutation. Anchor count 1 on each.
mutation on V2Layout.tsx |
result |
|---|---|
M1 if (phone && readLastPodId()) return; → if (phone) return; (the old behaviour) |
2 red — both new tests |
M2 → if (false) return; (phone always redirects) |
2 red — including the pre-existing "on a phone /v2 is the pods list page" |
M3 drop { replace: true } |
1 red — the back-bounce test |
M4 remove writeLastPodId(paramPodId) |
2 red — including the new first-login test |
The guard is pinned in both directions, which is what this change needed: M1 catches losing the new behaviour, and M2 catches over-applying it and stealing the list from a returning phone user. A one-sided test would have allowed the second.
M4 is the one I want to call out. Your comment argues the redirect is self-limiting because landing on the thread sets LAST_POD_KEY via the paramPodId effect. That is a claim about a different effect than the one you changed, so it could easily have been prose. It is not: deleting that write reds the new first-login test. The "and the list is where you come back to" half of the test name is doing real work.
No gap found. Nothing further from me.
At 390 a new account stopped on the pods list: the auto-select effect returns early on `phone` (the #1578 direction-C rule, "the phone never redirects — /v2 is the pods list page there"), so the workspace thread with Scout's welcome and the composer was one tap away and easy to miss. 18 of 29 recent signups never sent a first message. The list is the right landing for a phone that has a pod to return to, so that stays. It is the wrong landing for a device that has never opened one, which is every first login — there the list has nothing to return to. So the phone runs the same resolution (own invite-only workspace, else pods[0]) for that one case, with `replace`. The redirect is self-limiting: it lands on the param shell, whose paramPodId effect writes LAST_POD_KEY, so the next /v2 finds the key set and stays on the list, and `replace` means browser-back does not re-resolve it either. No new screen, no toast, no banner; the first-run hero keeps rendering where it does. Two jsdom cases on V2Layout with the phone media query mocked (both named in ux-lead's ruling), plus the pre-existing "phone /v2 is the pods list page" case for the device that does have history. Mutation-proven three ways: restoring the early return reds both new cases, ignoring the device reds the pre-existing one, and dropping `replace` reds the back-step case.
f196bbd to
3f4f09b
Compare
Closes TASK-144. Implements ux-lead's ruling on the row.
Cause
V2Layout's auto-select effect returned early onphone— the #1578 direction-C rule, "the phone never redirects:/v2is the pods list page there." So at 390 a first login stopped on the pods list, with the workspace thread (Scout's welcome, the composer) one tap away.Change
The list is the right landing for a phone that has a pod to return to, and that is untouched. It is the wrong landing for a device that has never opened one — every first login — so that one case runs the same resolution as desktop (own invite-only workspace, else
pods[0]) withreplace.Why it cannot loop: the redirect lands on the pod route, which mounts the param shell, and that instance's
paramPodIdeffect writesLAST_POD_KEY. The next/v2— the thread's back arrow — finds the key set and stays on the list.replacemeans browser-back does not re-resolve the landing either. No new screen, no toast, no banner, no change to where the first-run hero renders.Proof
Mutation-proven in three directions, because the rule is two-sided and one of its halves is a router verb:
phoneearly-return restored (old guard)phonealways redirects)/v2must stay on the list)replace→ pushRuling case (1) —
LAST_POD_KEYset,/v2renders the list and does not navigate — was already pinned by the pre-existing phone test and stays green. Suite neighbours that mount the shell (v2-layout-invariants,V2InviteDmGate,V2FirstRunHero): 4 suites, 146/146.Limit
jsdom cannot observe
replacedirectly, so that case pins the property it exists for: one step back does not return to/v2. The assertion goes through the router's own history rather than inspecting the navigate call.Not verified here: the 390 rendering gate — that is ux-lead's, and this seat has no browser.