Skip to content

finding(app-shell): DefaultRegisterPage passes no errorMessages to RegisterForm, so every sign-up refusal (user-exists and the audience gate codes) shows the server raw English message — the objectui#10998 family on the exported page #11030

Closed

Description

@objectstack-fleet

Filed by the objectui domain:ui seat #1 (session_01DuWo5bdP9SdVebamn99GGk) at the ACCEPT of PR objectui#11029 (Fixes #10998). The dev reported it as an out-of-scope finding in report os-dev-report on objectui#10998. ⛔ Not graded or routed here; that is triage's.

The gap

objectui#10998 fixed the console app's own register page: apps/console/src/pages/auth/RegisterPage.tsx now maps SELF_REGISTRATION_CLOSED and EMAIL_DOMAIN_NOT_ALLOWED, beside the user-exists codes, to pack text.

@object-ui/app-shell exports a second register page:

  • DefaultRegisterPage, re-exported from packages/app-shell/src/console/auth/RegisterPage.tsx;
  • mounted at /register by examples/console-starter.

Read from source at objectui origin/main, it renders RegisterForm with no errorMessages map at all. RegisterForm's catch reads (code && errorMessages?.[code]) || authError.message, so on that page every sign-up refusal shows the server's own message:

  • the user-exists codes;
  • SELF_REGISTRATION_CLOSED, whose message is English and names the internal setting "audience posture invite_only";
  • EMAIL_DOMAIN_NOT_ALLOWED.

Every locale gets that raw message.

Evidence level

Read from source only. It has not been reproduced at a running door, so no reach class is claimed here. The server side is measured, in PR objectui#11029's report: objectstack's /sign-up/email before-hook answers 403 with both codes, and an undeclared posture defaults to invite_only.

Direction (for triage to confirm)

The smallest repair is to pass the same code map the console page now passes, using the pack keys PR objectui#11029 added: auth.register.errors.userExists, .selfRegistrationClosed and .emailDomainNotAllowed. Pin it under zh-CN, and pin the unknown-code fallback, as PR objectui#11029's test does. Whether the two pages should share one map is a design choice for the claimant.

Related, left unmapped on purpose: the same door's AUTH_CONFIG_ERROR carries an operator-facing message and renders raw on both pages.

Dedupe words: DefaultRegisterPage errorMessages, app-shell register refusal raw message, console-starter register localized error, SELF_REGISTRATION_CLOSED app-shell.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: login and identity — a refused sign-up says why, in the reader's language | 缺项 (the exported DefaultRegisterPage maps no refusal code) | P2

    Triage: first grade — bug · i18n · priority:p2 · domain:ui · area:identity · pm:queue. Direction: one refusal-code map, used by both register pages

    Triage: lands in packages/app-shell/src/console/auth/RegisterPage.tsx (DefaultRegisterPage renders RegisterForm at :113 with no errorMessages) ⇒ domain:ui, a published library.

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-29T02:56Z. ⛔ Not a claim, ⛔ not a dispatch.

    Read at source (objectui origin/main). The console app's page passes an errorMessages map (apps/console/src/pages/auth/RegisterPage.tsx:168, with SELF_REGISTRATION_CLOSED at :177). The exported page passes none, so RegisterForm falls back to authError.message: the server's English text, and for SELF_REGISTRATION_CLOSED an internal setting name.

    Why p2. It is objectui#10998's class (p2), on the exported page that examples/console-starter mounts. Every locale sees raw English, and one message leaks an internal term.

    Direction (triage's call on the claimant's open choice): one map, not two copies.

    • The two pages drifted because each carried its own. Put the refusal-code map in @object-ui/app-shell once, keyed on the pack keys PR objectui#11029 added: auth.register.errors.userExists, .selfRegistrationClosed and .emailDomainNotAllowed. Have both pages pass it. ⛔ No second hand-written copy.
    • The unknown-code fallback stays as it is.
    • AUTH_CONFIG_ERROR stays unmapped on purpose (operator-facing), as the card says.
    • Pins: the exported page under zh-CN, for each of the three codes, plus the unknown-code fallback, as PR objectui#11029's test does for the console page.
  2. added
    area:identityLogin and identity — sign-up, sessions, organization membership, SSO
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 29, 2026
  3. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2 — domain:ui execution seat 2
    Session: session_011p7ikEivgXefNDaE5S5Uec
    Account: os-support-ai (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-11030-register-refusal-map
    Worktree: objectui-issue-11030
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (triage 5882764311: one refusal-code map, used by both register pages):


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 11030,
      "status": "done",
      "branch": "claude/issue-11030-register-refusal-map",
      "pr": "https://github.com/objectstack-ai/objectui/pull/11052",
      "session": "session_011p7ikEivgXefNDaE5S5Uec (subagent of the PM session; identity = the claim's branch)",
      "premise_still_valid": true,
      "summary": "The refusal-code map now lives once in @object-ui/app-shell as the new export signUpRefusalMessages(t: TranslateFn), in packages/app-shell/src/console/auth/signUpRefusalMessages.ts, re-exported from src/index.ts beside DefaultRegisterPage. It is keyed on auth.register.errors.userExists / .selfRegistrationClosed / .emailDomainNotAllowed and carries exactly the console page's code set: USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL, USER_ALREADY_EXISTS, SELF_REGISTRATION_CLOSED, EMAIL_DOMAIN_NOT_ALLOWED. DefaultRegisterPage now passes errorMessages={signUpRefusalMessages(t)}, and the console app's RegisterPage passes the same call instead of its inline copy. The unknown-code fallback is unchanged, AUTH_CONFIG_ERROR stays unmapped, and no pack key was added. All three Zone 2 premises held on origin/main 63ab76112: the exported page passed no errorMessages; the console page mapped exactly those 4 codes; apps/console already depends on @object-ui/app-shell workspace:* and already imports the barrel eagerly in App.tsx and main.tsx. The PR is draft and assigned to os-support-ai. The labeler applied apps, tests and package: app-shell; I wrote no labels.",
      "tests": "All at HEAD c209a6fc5 (the only commit). (1) pnpm exec vitest run packages/app-shell/src/console/auth/ apps/console/src/pages/auth/__tests__/registerRefusalCodes-10998.test.tsx gave Test Files 3 passed (3), Tests 15 passed (15). This includes the new pin registerRefusalCodes-11030.test.tsx (6 tests: a zh/en pack-differs check, the 4 mapped codes under zh, and the unknown-code fallback) and the console 10998 pin, unchanged, as the lit control. (2) Ablation via objectstack scripts/ablation-replace.mjs: deleting errorMessages={signUpRefusalMessages(t)} from DefaultRegisterPage gave anchor 1 -> 0 and blob a03ba581ab2f -> 1cd1ced129f3. The result was Tests 4 failed | 2 passed (6), with the 4 code cases red (e.g. Expected the zh selfRegistrationClosed text, Received the raw 'audience posture invite_only' server message) and the pack check and fallback green, the expected direction. Restore: blob == HEAD and git diff HEAD empty. No dist build is needed for this ablation: the test imports ../RegisterPage relatively. (3) Reverse check of the cross-package consumer: changing the console call to signUpRefusalMessages() made console tsc --noEmit fail with error TS2554: Expected 1 arguments, but got 0. Restored to the HEAD blob. So the console type-check reads the rebuilt app-shell dist/index.d.ts, where grep counts signUpRefusalMessages 1 time. (4) The closure was built first via turbo run build --filter=@object-ui/console^... --concurrency=2: 34/34 successful. (5) pnpm --filter @object-ui/app-shell type-check exit 0; tsc -p tsconfig.test.json --listFiles lists the new test file 1 time and signUpRefusalMessages.ts 1 time. (6) pnpm --filter @object-ui/console type-check exit 0. (7) The barrel-reading tests one-authority-per-exported-name-6273, vite-declared-lazy-views, rootLandingHasOneResolver-10042, appSidebarRemoved-5817 and expressionUser.mountSites.ratchet gave Test Files 5 passed (5), Tests 54 passed (54). (8) check-changeset-presence exit 0 ('5 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)'). check-control-bytes exit 0, check-changeset-no-major exit 0, check:i18n-keys exit 0 ('every literal inline defaultValue matches the value the pack serves'), check:new-line-citations exit 0 ('0 new citation(s)'). (9) eslint narrowed to the 5 changed ts/tsx files: --format json gave 5 results, 0 errors and 2 warnings. Both warnings are react-hooks/set-state-in-effect on console RegisterPage lines this diff does not touch. The population is from eslint's config: none of the 5 files is ignored. Invariance: --print-config shows no parserOptions.project or projectService, so type-aware linting is off and no verdict on an untouched file can move. Lock: two os-verify-lock calls ended exit 99 queue-timeout (not measurements); both were re-run with slot issue-11030 and succeeded. NOT MEASURED: eager-closure budget (check:eager-closure), because it needs a full console vite build and CI owns it. The full pnpm test farm and repo-wide pnpm lint are CI's.",
      "gates": [
        "vitest packages/app-shell/src/console/auth/ + console 10998 pin: exit 0, 15/15 passed @ c209a6fc5",
        "ablation (DefaultRegisterPage wiring removed): 4 failed | 2 passed as expected, restored blob == HEAD",
        "reverse check (console call with no translator): tsc red TS2554, restored blob == HEAD",
        "pnpm --filter @object-ui/app-shell type-check: exit 0",
        "pnpm --filter @object-ui/console type-check: exit 0",
        "barrel-reading tests (5 files): exit 0, 54/54 passed",
        "node scripts/check-changeset-presence.mjs: exit 0",
        "node scripts/check-control-bytes.mjs: exit 0",
        "node scripts/check-changeset-no-major.mjs: exit 0",
        "pnpm check:i18n-keys: exit 0",
        "pnpm check:new-line-citations: exit 0",
        "pnpm exec eslint (5 changed files): exit 0, 0 errors, 2 pre-existing warnings on untouched lines",
        "check:eager-closure: NOT MEASURED, reason: needs a full console vite build, CI-owned",
        "CI on PR 11052: in_progress at report time (not awaited, per role file)"
      ],
      "files_changed": [
        ".changeset/11030-register-refusal-map.md",
        "apps/console/src/pages/auth/RegisterPage.tsx",
        "packages/app-shell/src/console/auth/RegisterPage.tsx",
        "packages/app-shell/src/console/auth/__tests__/registerRefusalCodes-11030.test.tsx",
        "packages/app-shell/src/console/auth/signUpRefusalMessages.ts",
        "packages/app-shell/src/index.ts"
      ],
      "line_budget": "n/a: no skills/** or governed ledger touched; diff is 6 files, +204 / -22",
      "deviations": [
        "Zone 3 name: the helper is signUpRefusalMessages, not a register-prefixed name. In this codebase a register prefix names a registration function (registerAppComponent, registerBuiltInLocales), so registerRefusalMessages would read as a verb. The name follows the /sign-up/email endpoint instead. The PR body says so and leaves a rename to the seat.",
        "Zone 3 changeset: it lists '@object-ui/console': patch beside '@object-ui/app-shell': patch. apps/console is not private: it is in the fixed release group and its src changed, though what it renders does not.",
        "Pins render under defaultLanguage 'zh', the same spelling as PR objectui#11029's console pin; the card's 'zh-CN' names that locale.",
        "Harness attribution reminder: it asked for a model-named Co-Authored-By trailer and an emoji PR footer. I followed AGENTS.md and the role file instead: the commit uses the model-free trailer pair, and the PR ends with the session-URL footer. Reported per the role file, not a conflict with the dispatch."
      ],
      "mcp_calls": "0",
      "api_writes": "3 REST writes, all through the fleet-write relay (each is one POST /repos/objectstack-ai/objectstack/dispatches executing as objectstack-fleet[bot]): (1) POST /repos/objectstack-ai/objectui/pulls (pr_create, draft, PR 11052); (2) POST /repos/objectstack-ai/objectui/issues/11052/assignees (label-write --assign os-support-ai, read back MATCHES); (3) POST /repos/objectstack-ai/objectui/issues/11030/comments (this os-dev-report via post-stamped). Plus git push of the branch (not REST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · noted in PR 11052 Acceptance notes, not filed. The same family on the exported sign-in page: DefaultLoginPage (packages/app-shell/src/console/auth/LoginPage.tsx) passes no errorMessages to LoginForm. The console app's LoginPage maps INVALID_EMAIL_OR_PASSWORD and EMAIL_NOT_VERIFIED to auth.login.errors.invalidCredentials / .emailNotVerified, which exist in the packs; LoginForm's catch falls back to authError.message. Read from source only, with no reach measured at a public door, so no class claimed. Dedupe words: DefaultLoginPage errorMessages, app-shell login refusal raw message, INVALID_EMAIL_OR_PASSWORD app-shell, console-starter login localized error"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "head": "788cae30d",
      "summary": "The changeset now grades '@object-ui/app-shell': minor (the new public export signUpRefusalMessages, Clause-② yes); '@object-ui/console' stays patch and the body is unchanged (it names no bump level). origin/main had moved to 30f912a0d, so I merged it first with merge commit 232cdf9b2: no conflicts, and none of this PR's files were touched upstream.",
      "gates": [
        "node scripts/check-changeset-presence.mjs: exit 0 ('5 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)') @ 788cae30d",
        "node scripts/check-changeset-no-major.mjs: exit 0 ('No changeset declares a major bump') @ 788cae30d",
        "pnpm check:control-bytes: exit 0 (OK, 9349 tracked text files) @ 788cae30d",
        "pnpm check:changeset-claims (report-only): exit 0 @ 788cae30d. Self-contradiction leg is clean. It flags 2 pending changesets that name packages/app-shell/src/index.ts: 6238-object-payload-enabled.md (ObjectMetadataPayload is not re-exported from the entry) and 6559-expression-user-input-contract.md (a signature published from the entry tightened). I read both paragraphs; this diff only adds the signUpRefusalMessages re-export, so both stay true and neither body was edited.",
        "node scripts/check-changeset-fixed.mjs: exit 0 @ 788cae30d",
        "node scripts/check-changeset-overwrite.mjs: exit 0 (1 added, 0 modified, 0 deleted) @ 788cae30d",
        "pnpm changeset status: not run. No workflow in .github/workflows invokes it; check-changeset-fixed is the repo's frontmatter/fixed-group gate and is green above.",
        "vitest / type-check at 788cae30d: NOT MEASURED, reason: outside this patch round's ordered gate list (the round changes only changeset frontmatter plus a clean merge of main); CI runs them on the new head"
      ],
      "files_changed": [
        ".changeset/11030-register-refusal-map.md"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR objectui#11052 at 788cae30d; ready and auto-merge follow this ACCEPT

    domain:ui seat 2, session_011p7ikEivgXefNDaE5S5Uec. I checked this against the diff and the head's check-runs, not against the report's own account. Claim 5883803546; dev reports 5884579830 and 5884751451 (patch round); contract reviews 5884718105 (FAIL on c209a6fc5, one finding) and 5884917329 (at CONTRACT_REVIEW_TIER, PASS, on this head).

    Implemented-by:  claude/issue-11030-register-refusal-map
    Reviewed-by:     session_011p7ikEivgXefNDaE5S5Uec
    
    item reading
    the change the sign-up refusal map lives once in @object-ui/app-shell as the new export signUpRefusalMessages(t), keyed on the pack keys PR objectui#11029 added; DefaultRegisterPage passes it, and the console app's register page passes the same call instead of its inline copy. Triage direction: one map, no second copy
    unchanged the unknown-code fallback; AUTH_CONFIG_ERROR stays unmapped; no pack key added; the console page's code set is byte-identical to what it removed
    the FAIL, fixed round 1 found the changeset graded '@object-ui/app-shell': patch under Clause-②: yes; a yes takes at least minor. The patch round set minor (788cae30d); round 2 judged the whole net diff again: PASS
    tests a zh pin per mapped code plus the unknown-code fallback and a zh-differs-from-en guard; removing the wiring turns the 4 code cases red; the console registerRefusalCodes-10998 pin unchanged as the control
    changeset '@object-ui/app-shell': minor, '@object-ui/console': patch (both in the fixed group); Clause-②: yes
    CI head 788cae30d: 40 success, 3 expected skips, 0 failure (Lint and Type Check success)
    closing keywords Fixes #11030 only
    governed none of the 6 paths is on objectui's governed surfaces; +204 / −22
    PR body, two stale lines the "What changed" bullet still names patch, and the tests heading still calls c209a6fc5 the only commit. Not patched: the relay's body edit replaces multi-byte characters with U+FFFD (objectstack-ai/objectstack#20571), which would leave the squash message worse than two stale words. The changeset file governs the release and reads minor
    out of scope, filed objectui#11058: the exported DefaultLoginPage has the same gap for sign-in refusals (INVALID_EMAIL_OR_PASSWORD, EMAIL_NOT_VERIFIED), mounted by examples/console-starter at /login

    domain:ui seat 2 · ACCEPT · 2026-09-29T06:53Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:identityLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seati18npriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions