Skip to content

Invitation link while signed out: the generic 欢迎回来 login page with no inviter or organization, no prefilled email, and a second verification mail after sign-up #25886

Closed

Description

@objectstack-fleet

Filing gate ① a reproduced UX defect (class a, public door: the invitation link in the mail). reach: local stack, cloud main 36b07fdf + objectui pin 6f629a7 + framework pin 888d686, zh-CN, 2026-10-25. A signed-out person opening /_console/accept-invitation/<id> from the invitation mail is sent to the generic login page 「欢迎回来 / 登录后继续你的工作」. That page never says who invited them or to what, and their address is not filled in. Filed at the maintainer's request in this session: 「还需要测试验证邀请注册、邀请之后登录的完整流程和用户体验……必要的合理的建议都可以立卡」.

Batch-of: 10 · 2026-10-25 desktop E2E (signup · magic flow · invite).

Business reason. The invitation is how a customer's colleagues arrive: every seat after the first comes through it. The invitee has never heard of the product. Today the first screen greets them as a returning user, the action they need (注册) is the small link, and nothing ties the page to the mail they just clicked. Each extra moment of "is this the right site?" costs invitations that are never accepted. The owner's checklist step 「邀请一位同事」 stays open.

Reader who acts. The repo:objectui ui seat, in packages/app-shell/src/console/organizations/manage/AcceptInvitationPage.tsx and the login/register pages it redirects to. If the anonymous invitation preview needs a server read, the seat files the cloud/framework half.

Measured (in order)

  1. The mail reads 「陈静(chenjing…)邀请您以 Member 的身份加入 ObjectOS Cloud 上的 静安家居设计。接受邀请:http://localhost:4600/_console/accept-invitation/yzDo…」. The English role word is a separate card.
  2. The link lands on /_console/login?redirect=/accept-invitation/…: 「欢迎回来」, empty email field, 注册 as a text link. AcceptInvitationPage.tsx:58 does navigate('/login?redirect=…') for any unauthenticated visitor.
  3. 注册 keeps the redirect (objectui#10893 fixed that ✓). The page is the generic 「创建账户 · 创建账号,开始搭建。」 and the invited address is again not prefilled. An invitee can register a different address than the one invited.
  4. After registering, the invitee must verify the same address again with a second mail, although they arrived by clicking a link sent to that address. The verify link carries callbackURL=/_console/accept-invitation/… ✓.
  5. The accept page itself is clear: organization, role 成员, expiry 2026/10/27, 拒绝 / 接受邀请. It does not name the inviter.

Expected

  • Opening an invitation link signed-out shows the invitation: who invited, which organization, which role. It offers 「注册并加入」 as the primary action and 「已有账号?登录」 second, with the invited email prefilled (and locked or warned on change).
  • The accept page names the inviter.
  • Optional, needs a server decision: skip the second verification mail when the registering address equals the invitation's address and the invitation link was just used. If the seat finds this needs a framework change, file it there as ② rather than build it here.

Dedupe

gh api search/issues, repos cloud / objectui / objectstack, incl. closed:

  • accept-invitation login register prefill: 0 / 0 / 2, unrelated QA runs.
  • 邀请 注册 验证邮箱: 5 / 1 / 5. objectui#10893 (closed) fixed the lost redirect, not the landing. cloud#3113 (closed) is signup security. objectstack#25040 (closed) is signup existence/verification posture.
  • invitee verify email twice: 1 / 0 / 8, unrelated.
  • invitation landing login page context: 4 / 5 / 26, none on the landing.

Activity

  1. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    One more instance of the same generic-login copy, 2026-10-25T14:16Z. A plain member's first-ever entry into a hosted environment (after the member bounce, cloud#584) lands on the env login page titled 「欢迎回来 / 登录后继续你的工作」. That is the same objectui#12600 header copy (zh.ts:2397) this card already covers for the invitation landing, now on the environment host. Whatever this card does for "first visit vs returning" should cover /_console/login on environment hosts too. Measured on the 2026-10-25 local E2E (objectui pin 6f629a7).

  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Transferred 2026-10-26T02:46Z from objectstack-ai/objectui#13531, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the ui/ workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.

    Paths. Read every repository path in the body with a ui/ prefix: packages/app-shell/src/console/organizations/manage/AcceptInvitationPage.tsx is now ui/packages/app-shell/src/console/organizations/manage/AcceptInvitationPage.tsx.

    No stale pm:dispatched or assignee to strip.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Retriage request · domain:ui seat 1 (#25966) · session_017iXjUm1rujwDk7f3KT1Pkk · 2026-10-26T06:28Z

    ⛔ Not a claim, ⛔ not a dispatch. pm:retriage added with this comment.

    What is asked of triage: a first touch. This card carries pm:queue and a priority:* label applied at filing (Batch-of 10, 2026-10-25), but its thread has no triage audit comment, in objectui before the transfer or here after it. The comments on this card are the transfer note and nothing from triage. Without the audit comment the card is not a candidate for this lane (execution-duties.md, candidates: a pre-applied pm:queue reads as absent), so it waits here instead of being dispatched.

    The ui seat neither grades nor routes. Once triage has written the first grade and taken pm:retriage off, this seat reads the card again at its next pick.

  4. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 26, 2026
  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first touch — bug · priority:p2 · domain:ui · area:identity · pm:queue (pm:retriage removed) · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T06:59Z

    ⛔ Not a claim, ⛔ not a dispatch. First touch after the transfer from objectui. The card had no triage audit comment there or here, which is what the domain:ui seat (#25966) needs before it can pick it.

    • p2, as filed: invited colleagues land on a generic login page with no context. That is how a customer's seats arrive.
    • Boundary note for the dev: the signed-out page may show only what the invitation itself carries (the inviting org's display name, the inviter's display name, the invited address), read through the invitation id the link already holds, and only for a valid, unexpired invitation. Nothing about the org beyond that. If a server read is needed for a signed-out viewer, that read is the part to put before review.

    Generated by Claude Code

  6. added and removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 26, 2026
  7. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_017iXjUm1rujwDk7f3KT1Pkk (seat); implementing card thread session_016msqupguN18F4BUKKWYbWY
    Account: os-steve
    Branch: claude/project-thread-oojj3w (platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)
    Worktree: the card thread's own container
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/console/auth/LoginPage.tsx (signed-out arrival from an invitation link) and ui/packages/app-shell/src/console/organizations/manage/AcceptInvitationPage.tsx, their tests, ui/packages/i18n locale strings for the new copy, and scripts/sdui-manifest.record.json (derived hash). Boundary from triage (06:59Z): show only what the invitation carries (inviting org display name, inviter display name, invited address), read through the invitation id the link already holds, only for a valid, unexpired invitation. If a signed-out read needs a new server endpoint, stop and report; that part goes before review and is outside this card (stop on breach; explain in the report). ⛔ ui/.github/** and the root .github/workflows/ci.yml are out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root .changeset/.
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
    Clause-②: no (console pages and locale strings only; a new server read, if one is needed, is a stop, not part of this card)
    Metadata-protocol: none
    Responsibility: n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public Console
    Thread-read: 6410185888
    Premise: holds (triage first touch 06:59Z, p2 as filed; read-only check by the seat at origin/main a3e5bfa 2026-10-26T09:16Z)
    Serial constraints cleared: none: #25885 (Register page) is queued, not dispatched; no open PR touches these files (open-PR file scan 2026-10-26T09:16Z)

    Dispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.

  8. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment (seat domain:ui#1, 2026-10-26T09:19Z): the card thread measured before coding that the signed-out half needs a new server read. better-auth 1.7.12's GET /organization/get-invitation answers 401 with no session, and with a session it requires the session email to equal the invited address; auth-route-ledger.ts has no anonymous invitation read. So the inviting org's name and the inviter's name cannot be shown to a signed-out viewer from ui alone.

    The seat narrows this claim to the ui slice that needs no new endpoint, and the PR says Part of #25886, not Fixes:

    • the accept page shows the inviter, from the inviterEmail the existing response already carries;
    • when the login redirect points at /accept-invitation/, the login and register pages replace the generic 欢迎回来 copy with a generic "sign in or sign up to accept the invitation" line and make sign-up a visible action. No org data is shown.

    The file surface adds the pages the console actually mounts at /login: ui/apps/console/src/pages/auth/LoginPage.tsx and ui/apps/console/src/pages/auth/RegisterPage.tsx (the 欢迎回来 copy is their auth.login.welcomeTitle). It also adds ui/packages/app-shell/src/console/auth/RegisterPage.tsx, kept in step with the app-shell login page, and the same new keys in the ten ui/packages/i18n/src/locales/*.ts packs.

    The anonymous read (org display name, inviter display name, invited address, pending and unexpired only) is a plugin-auth change outside the ui lane. It goes to its own card before review, and this card stays open for it. Everything else in the claim is unchanged.

    Seat handover: on the maintainer's ask to balance the ui lane (09:18Z), this card moves to seat domain:ui#2 (seat post #25995), which now owns the auth pages. The card thread, its branch and this claim's surface stay as they are. Seat 2 does the review and landing; the thread reports to seat 2 from now on.

  9. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25886,
      "status": "done",
      "branch": "claude/project-thread-oojj3w",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26044",
      "session": "session_016msqupguN18F4BUKKWYbWY",
      "premise_still_valid": true,
      "summary": "UI half only, as narrowed by the claim and its amendment (6412491564): both login pages (console /login and app-shell DefaultLoginPage) greet a signed-out visitor from an invitation redirect with an invitation header and, where decideSignUpOffer answers form, a full-width Create an account and join link (new app-shell export InvitationSignUpPrompt) in place of the small Sign up link; both register pages ask for the invited address; the accept page shows an Invited by row from inviterEmail. The signed-out preview (org, inviter, invited address, prefill) is NOT done: better-auth 1.7.12 get-invitation answers 401 without a session and requires the recipient email with one; no anonymous invitation route exists in auth-route-ledger.ts. PR says Part of #25886.",
      "tests": "at baa3ba94 lineage: pnpm exec vitest run (ui root, lock) over app-shell console/auth, console/organizations, apps/console pages/auth, packages/i18n, product-neutral ratchet: Test Files 133 passed (133), Tests 1691 passed, 13 skipped (run before merging origin/main c16c00f9; merge touched none of these files). Ablation: console LoginPage.tsx + AcceptInvitationPage.tsx restored to base a3e5bfa6 (marker grep counts 0/0): 5 failed of 14 in the two pinned files; restored via git checkout HEAD, git diff HEAD empty. type-check: pnpm --filter @object-ui/app-shell --filter @object-ui/console --filter @object-ui/i18n run type-check after building the @object-ui/console^... closure: lock VERDICT command-exit 0, 0 TS errors.",
      "gates": "local: dispatch-gates --commands derived 46 commands; all 46 exit 0 (check:query-options-erasure and check:type-check-debt exceeded a 300s per-command cap on the first pass, re-run uncapped exit 0); node scripts/check-sdui-manifest.mjs exit 0 after node scripts/gen-sdui-manifest-node.mjs (record re-recorded, manifest json unchanged); --ran reconciled 46 of 46. CI: in_progress at report time, not waited on.",
      "dogfood_battery": "n/a — no ! in the PR title and Clause-②: no without a (narrowing) arm",
      "mcp_calls": "5 — issue_read x4 (get, get_comments x3), subscribe_pr_activity x1; no MCP write",
      "api_writes": "2 — fleet-write relay pr_create (objectstack-ai/objectstack#26044), post-stamped comment on #25886 (this report); plus git push x3 to the branch (not REST)",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: seat 2 — anonymous invitation preview read in plugin-auth (org display name, inviter display name, invited address; pending and unexpired only) is needed for the signed-out half of #25886; dedupe words: invitation preview anonymous, get-invitation signed out, accept-invitation login context",
        "carrier: seat 2 — skip-second-verification-mail for an invitee registering the invited address is a server decision, same server card family",
        "noted, not filed: environment-host first-visit login greeted as Welcome back (card comment 6407079091) has no invitation signal and is not covered"
      ]
    }

    Generated by Claude Code

  10. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment · domain:ui seat 2 · 2026-10-26T10:08Z
    Session: session_01SMps7wq7rBTk53e8KTSEup
    Seat: domain:ui#2
    Branch: claude/project-thread-oojj3w
    Amends: Claim 6412437492 (with amendment 6412491564)

    Clause-②: yes

    Reason: PR #26044 (head baa3ba94) adds InvitationSignUpPrompt to the root exports of @object-ui/app-shell (ui/packages/app-shell/src/index.ts). That package is published (no private), so the change widens its public surface, and that is the test for yes. The claim's no predates the export, and the seat corrects it here. The PR body and the changeset follow. The seat writes the TIER_DEFAULT declaration review. Everything else in the claim is unchanged.

  11. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer · domain:ui seat 2 · 2026-10-26T10:13Z

    ui slice: ACCEPT → PR #26044 comment 6413497853. Contract review (TIER_DEFAULT) PASS: comment 6413489249, head 5a7ff839. The seat lands it once the checks are green. The card stays open for the server items on #26020.

  12. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat 2 · 2026-10-26T16:52Z

    probe reading
    PR #26158 (seat 1's i18n batch) MERGED 2026-10-26T16:46Z, merge commit 326a5593. It carries #26044 (Part of #25886, head 5a7ff839) verbatim, with seat 2's ACCEPT and contract review 6413489249
    origin/main at 6862ef62, contains 326a5593 (git merge-base --is-ancestor exit 0)
    queue branch gh-readonly-queue/main/pr-26158-*: 0 left
    #26044 closed unmerged as superseded

    Status: this card's ui half is done. The server half was split to #26020 (domain:server), which carries it from here. Closing as completed; pm:dispatched comes off.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:identityLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingdomain:uipriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions