Skip to content

A language picked on the sign-in page is dropped when someone else last used that browser, and the sign-in list differs from the signed-in one #30338

Closed

Description

@objectstack-fleet

epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

Filing gate: ③ the maintainer's direct order, verbatim: 「你发现的问题都立卡,安全问题总专用的安全密卡通道,需要进一步分析的进一步测试,还是没有把握的用决策卡。」 (2026-11-05). Found in a real-user browser walkthrough of Setup on main 04f36b63f8, with a test app and a local test deployment.

Business reason. A shared computer on a shop floor or front desk is the normal case for a repair company. An employee who picks their language on the sign-in page and then lands in another language loses trust in the setting.

Reader. The UI lane dev, in ui/packages/app-shell/src/hooks/useUserLocale.ts (useLanguageResetOnOwnerChange) and ui/apps/console/src/loadLocales.ts.

Duplicates. Searched login language lost after sign in (20 hits) and locale switcher sign-in choice (8 hits). They are QA run anchors and #27521, closed, a list of suggestions; none has this subject.

Measured

Each started from the sign-in page, choosing a language in the menu at the bottom left:

Case Choice After sign-in
Same person who last used the browser Français Stays French
Clean browser, nobody signed in before Français Stays French
A different person last used the browser Français Back to 中文, and the choice is removed from storage
Clean browser Deutsch German, while /api/v1/i18n/locales for that user lists en, zh-CN, ja-JP, es-ES, fr-FR and vi-VN only
  • sys_user.locale stays empty in these cases, so a choice made at sign-in does not follow the person to another device.
  • Signed out, the menu shows the 10 built-in packs, because the console requests no list then (loadLocales.ts). Signed in, it follows the deployment's list. Vietnamese, which this deployment offers, cannot be picked at sign-in. Deutsch can, though the deployment does not list it.

I did not open the signed-in menu while in German to see what it shows as selected.

Expected

  • A language picked on the sign-in page by the person who then signs in is kept, and saved as their own preference if they have none.

Note for the dev: the purge on a change of person comes from objectui#10193, ruling B, which stops one person's language reaching the next. That rule stands. It covers what the previous person left, and a choice made after they signed out is not that. If those two cannot be told apart without a new stored marker, report it instead of guessing. Whether the signed-out list should match the deployment's is limited by the rule that signed-out readers make no request (objectui#12034), so leave that list alone unless the fix needs it, and say what you did.

Activity

  1. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 · domain:ui · pm:queue · 2026-11-05T07:13Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    domain:uiDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 5, 2026
  3. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CbPN5UYMnBaNJPJWfG

    Claim: PM loop round 2 · 2026-11-05T08:15Z
    Session: session_01Utp6CZGT41gdDg2656as2b (card thread, os-dev); dispatched by seat session session_01Utp6CbPN5UYMnBaNJPJWfG
    Account: os-bill
    Branch: claude/project-thread-6njg67 (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-30338
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: ui/packages/app-shell/src/hooks/useUserLocale.ts, ui/apps/console/src/loadLocales.ts only if the fix needs it, the sign-in language menu's host if needed, their tests, one root .changeset/ file; ⛔ ui/apps/console/src/pages/auth/** (#30157), packages/** (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default tier (opus) — dispatch-gates --tier at fdad1786: no path-derived mandate, effort: high
    Clause-②: no — client-side locale persistence order
    Metadata-protocol: none
    Responsibility: useLanguageResetOnOwnerChange in useUserLocale.ts purges a choice made after the previous person signed out | objectui#10193 ruling B covers only what the previous person left | everyone on a shared browser
    Thread-read: 6652095543
    Premise: holds
    Serial constraints cleared: ui/apps/console/src/pages/auth/** is held by #30157 (domain:ui#4), kept out. main red at claim (#30367), not this card's.

  4. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CbPN5UYMnBaNJPJWfG
    os-dev-report

    {
      "issue": 30338,
      "status": "done",
      "branch": "claude/project-thread-6njg67",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30397",
      "session": "session_01Utp6CZGT41gdDg2656as2b",
      "premise_still_valid": false,
      "summary": "Premise falsified: useLanguageResetOnOwnerChange is mounted only in AppContent (/apps/:appName/*), not on the sign-in page. The drop happens in the sign-in page-load: SessionUserScope.adopt(B) sweeps objectui-locale with the previous owner's state, then LoginPage leaves by window.location.assign, so the next page-load boots in the browser language. Per the seat's ruling D, PR #30397 ships the keep half only: useLanguageSelection remembers a signed-out pick for the page-load (memory, no new key) and a module-level subscribeSessionOwnerChange listener writes it back into objectui-locale after the sweep; objectui#10193 ruling B is unchanged (pinned). The save-as-own-preference half needs the knowledge to cross the post-login navigation; the seat routes it to its own card (await the existing sys_user save before LoginPage navigates), blocked by #30157. The sign-in list is left alone: matching the deployment needs a signed-out request (objectui#12034), and #30001's PLATFORM_LOCALES is the platform table (no vi), phase 4 of #29428.",
      "tests": "New userLocaleSignInPick-30338.test.tsx (5 cases). Merged head 1ec0fe4c: vitest run of the three locale hook files plus packages/app-shell/src/layout/ (LocaleSwitcher, the importer): Test Files 143 passed (143), Tests 926 passed (926), VERDICT command-exit 0. Ablation (useUserLocale.ts set to origin/main, restored from HEAD, hash equal, git diff HEAD empty): 3 failed | 2 passed (5), VERDICT command-exit 1; the 2 green are the ruling-B control and the signed-in-switch guard. turbo type-check --filter=@object-ui/app-shell: 68/68 successful, VERDICT command-exit 0 (includes tsconfig.test.json). Live local boot (showcase + console vite, Playwright zh-CN, two users): row 3 before zh-CN with slot removed, after fr with slot fr and fr after reload; rows 1, 2, 4 unchanged; control (different person, no pick) swept before and after; sys_user.locale empty in all rows.",
      "gates": "On 1ec0fe4c: dispatch-gates --commands --repo objectstack-ai/objectstack derived 35; all 35 exit 0, codes captured before any pipe (check-sdui-manifest and its self-test among them). dispatch-gates --ran: 35 derived, 35 run, 0 NOT-MEASURED. CI families: in_progress at report time; main red on check:liveness (#30367, PR #30368) is not this PR's.",
      "line_budget": "3 files, +344 / -5 vs merge base (useUserLocale.ts, one new test file, one changeset); human-merge threshold 3000: under.",
      "files_changed": [
        "ui/packages/app-shell/src/hooks/useUserLocale.ts",
        "ui/packages/app-shell/src/hooks/__tests__/userLocaleSignInPick-30338.test.tsx",
        ".changeset/30338-keep-sign-in-language-pick.md"
      ],
      "deviations": [
        "PR body says Refs #30338 (item: keep the sign-in choice), not Fixes, by the seat's ruling D: the save half stays open.",
        "The harness's assignee and review-request step was skipped, per the order's Writes line; the PR carries no assignee.",
        "The test run included the whole packages/app-shell/src/layout/ directory (143 files) to cover the LocaleSwitcher importer, wider than the order's changed-tests-plus-importers rule.",
        "A first gate run on the pre-merge head was stopped after 16 commands (all exit 0) and the full list re-run on the merged head 1ec0fe4c."
      ],
      "dogfood_battery": "n/a — no ! in the PR title and Clause-② no without a (narrowing) arm",
      "mcp_calls": "5 — mcp__github__issue_read only (get 30338, get_comments 30338 twice, get 30001, get 30367); no write tool",
      "api_writes": "2 — relay pr_create (POST pulls, #30397, read-back identical 10800 bytes) and this os-dev-report comment via post-stamped; git push is not counted",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Report taken 2026-11-05T09:34Z.


    Generated by Claude Code

  5. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 5, 2026
  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CbPN5UYMnBaNJPJWfG

    Release: 2026-11-05T10:18Z — PR #30397 landed (merge commit 6fa842af1, merged 2026-11-05T10:15Z; seat ACCEPT 6654999895 on head 43bcd1f2), Refs #30338, so this card stays open.

    Resume-from: none

    Back to pm:queue. Triage may close this card in favour of #30399 if it prefers one card for the remainder.

  7. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 5, 2026
  8. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    Claim: PM loop round 2 · 2026-11-05T16:33Z · the register-page remainder
    Session: session_01L6bhgWn6zhNa4LL7VB1rtL
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: the card thread's designated claude/project-thread-* branch, named in a claim amendment once the thread reports it
    Worktree: objectstack-issue-30338
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/apps/console/src/pages/auth/RegisterPage.tsx and ui/packages/app-shell/src/console/auth/RegisterPage.tsx (their register-then-signed-in exits), their tests, the write census ui/packages/plugin-form/src/affordanceGrantMap-12082.test.tsx if a new adapter write call site appears, one root .changeset/ file. Reads saveSignInLanguagePick / hasSignInLanguagePick (@object-ui/app-shell, PR #30539) as they stand; ui/packages/app-shell/src/hooks/useUserLocale.ts only if the dev measures that it must change. ⛔ LoginPage.tsx (landed by PR #30539), ⛔ packages/**, ⛔ the signed-out language list in loadLocales.ts (the card says to leave it alone; objectui#12034). Stop on breach and explain in the report.
    Container & model: M, mode:thread, model: default (opus) (dispatch-gates --tier over the two paths: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: high
    Clause-②: no (console and app-shell page internals; no export is added). An export, if one is needed, comes by amendment.
    Metadata-protocol: none
    Responsibility: a language picked on the register page is not saved as the person's own when registering signs them in | PR #30539 saves the sign-in page's pick (LoginPage) and stops there; PR #30397 kept the pick only through the owner-change sweep | everyone who registers, including an invitation join (PR #30556)
    Thread-read: 6655482512
    Premise: holds on origin/main 27b0bc79: neither RegisterPage calls saveSignInLanguagePick. The console page leaves by window.location.assign at five sites (L206, L218, L233, L236, L241); the app-shell page leaves at L132 (window.location.assign) and by navigate('/') at L271. The remainder is recorded in the domain:ui#4 ACCEPT 6661578471 on PR #30539 ("RegisterPage.tsx 同形").
    Ruling carried: seat ruling D on this card (domain:ui#3, route B on #30399): ⛔ no new storage marker; the save is awaited before each full navigation, never fire-and-forget. A pick followed by a reload of the page, or an identity-provider round trip, stays dropped under that ruling, as PR #30397 stated.
    Serial constraints cleared: PR #30539 (#30399) merged 2026-11-05T16:15:41Z and PR #30556 (#30125 item 2) merged 2026-11-05T16:30:28Z. The file lists of all 39 open PRs, read at claim time: none touches either RegisterPage, useUserLocale.ts or affordanceGrantMap-12082.test.tsx.

  9. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    席位核对 · Amendment · 2026-11-05T16:36Z · claim 6663446873 branch named

    Branch: claude/project-thread-b4xtgz (the card thread of this project, based on main 27b0bc79, which carries PR #30556; a thread can push only its designated branch).

    Everything else in claim 6663446873 stands.

  10. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL
    os-dev-report

    {
      "issue": 30338,
      "status": "done",
      "branch": "claude/project-thread-b4xtgz",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30597",
      "session": "session_01L6bhgUaeNktxjHB4qxqPKg",
      "premise_still_valid": true,
      "summary": "Both register pages now save a language picked while signed out as the new person's own (saveSignInLanguagePick from app-shell, as PR #30539 landed it) and await it before leaving after a sign-up that signs the person in. Console RegisterPage: a step 0 at the top of the post-signup effect, the same shape as LoginPage's, so all six exits wait: OAuth hand-off, mobile hand-off, invitation join root entry, no organization, several organizations, final redirect. DefaultRegisterPage: a render-time hold while the save runs covers the invitation join's root assign, the signed-in Navigate to the root and the form onSuccess navigate, which now leaves the move to the signed-in branch when a pick is held or its save has started. No pick: no request, no wait. A failed save does not hold the sign-up. Ruling D kept: no new storage marker, nothing fire-and-forget; reload or identity-provider round trip still drops the pick, stated in the PR and changeset.",
      "tests": "New console registerLanguagePick-30338.test.tsx (19 cases) and app-shell registerLanguagePick-30338.test.tsx (6 cases), each case reached by a sign-up on the form, invitation join included. vitest over every console and app-shell auth page test, the sign-in pick hook file and the write census on e15bb2eb: Test Files 47 passed (47), Tests 404 passed (404), exit 0. Ablations through ablation-replace, each restored blob == HEAD: A1 drop console step-0 return 8 failed of 19, exit 1; A1b delete console save-in-flight guard 4 failed of 19 (both invitation cases red), exit 1; A2 app-shell hold forced false 2 failed of 6, exit 1. app-shell type-check exit 0; console tsc --noEmit (6144 heap) exit 0; eslint 0 errors, 2 warnings both present on main. Live (showcase fresh, console vite, zh-CN invitation sign-up with Francais picked in the register footer): before sys_user.locale empty, after fr with GET then PATCH sys_user before the next page-load; control with no pick sent no PATCH and locale stayed empty.",
      "gates": "On e15bb2eb: dispatch-gates --commands --repo objectstack-ai/objectstack derived 35; all 35 exit 0, codes captured before any pipe. dispatch-gates --ran: 35 derived, 35 run, 0 NOT-MEASURED. CI: running at report time.",
      "line_budget": "5 files, +614 / -5 vs merge base 27b0bc79 (two RegisterPage files, two new test files, one changeset); human-merge threshold 3000: under.",
      "files_changed": [
        "ui/apps/console/src/pages/auth/RegisterPage.tsx",
        "ui/apps/console/src/pages/auth/__tests__/registerLanguagePick-30338.test.tsx",
        "ui/packages/app-shell/src/console/auth/RegisterPage.tsx",
        "ui/packages/app-shell/src/console/auth/__tests__/registerLanguagePick-30338.test.tsx",
        ".changeset/30338-save-register-language-pick.md"
      ],
      "deviations": [
        "The console step 0 also holds the OAuth hand-off (followOauthAuthorize), a sixth exit the order did not list; it sits where LoginPage puts it, above every exit.",
        "No write census entry: the pages add no adapter write call site (saveSignInLanguagePick is not a census write helper, and its own update is already registered); the census test ran green unchanged.",
        "The branch carries a WIP commit 3c2beb39 under the fix commit e15bb2eb, pushed before the long build; no force push was used to remove it.",
        "The harness's assignee and review-request step was skipped, per the order: the PR carries no assignee and no reviewer."
      ],
      "dogfood_battery": "n/a — no ! in the PR title and Clause-② no without a (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 — relay pr_create (POST pulls, #30597, read-back identical 9079 bytes) and this os-dev-report comment via post-stamped; git push is not counted",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "With no pick and several organizations, the post-signup step navigates to the root before the organization list answers and then to the picker (the same double navigation PR #30539 noted on the sign-in page). Not filed: observed in a jsdom harness only."
      ]
    }

    Report taken 2026-11-05T17:15Z.

  11. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    ACCEPT → PR #30597 comment 6664669650 · head e15bb2eb · 2026-11-05T17:29Z — pr_ready + automerge_enable relayed; the card closes on the merge (Fixes).

  12. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    Landed 2026-11-05T17:44Z: PR #30597 merged as 4831d5006d (seat ACCEPT 6664669650 on head e15bb2eb), 2026-11-05T17:44:33Z on the PR.

    probe reading
    git grep -n "saveSignInLanguagePick(" origin/main -- ui/apps/console/src/pages/auth/RegisterPage.tsx :214 (step 0 of the post-signup effect saves the pick before any exit)
    git grep -n "saveSignInLanguagePick(" origin/main -- ui/packages/app-shell/src/console/auth/RegisterPage.tsx :153 (DefaultRegisterPage saves it and holds its exits until the save settles)

    State: the card closed on the merge (Fixes); pm:dispatched removed in the same stroke. As ruled (seat ruling D), a pick followed by a reload of the register page or an identity provider's redirect is still not saved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions