Repository navigation
console: a refused self-registration shows the server's raw English message — RegisterPage maps only USER_ALREADY_EXISTS to localized text #10998
Description
Activity
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsPath: run it — the console in the user's language | 缺项 (
RegisterPage's error-code map) | P2Triage: first grade —
bug·i18n·priority:p2·domain:ui·pm:queueTriage: lands in
apps/console/src/pages/auth/RegisterPage.tsx⇒domain:ui.Rationale: a refused self-registration shows the server's raw English message, and that message exposes the internal term 「audience posture invite_only」 to an end user ⇒ p2, like the other zh-CN leftovers today.
Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-28T17:02Z. ⛔ Not a claim, ⛔ not a dispatch.Direction.
- Map
SELF_REGISTRATION_CLOSEDandEMAIL_DOMAIN_NOT_ALLOWED(both stable server codes) to localised end-user wording in the page's existing error-code map. - The raw server message stays the fallback for codes the page does not know.
- Pin: each code under zh-CN.
- Map
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Sep 28, 2026 objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsClaim: PM loop round 14 · an opus-only card while the review tier is unavailable
Session:session_01DuWo5bdP9SdVebamn99GGk
Account:os-steve(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-10998-register-refusal-codes
Worktree:objectui-issue-10998
Domain:domain:ui
Seat:domain:ui#1
File surface (triage grade5874758856):apps/console/src/pages/auth/RegisterPage.tsx: its error-code map gainsSELF_REGISTRATION_CLOSEDandEMAIL_DOMAIN_NOT_ALLOWED, each mapped to localized end-user wording. The raw server message stays the fallback for codes the page does not know.- The locale entries for the new keys, beside the existing
auth.register.errors.userExists, in every locale pack that carries that key. - A pin: each code under zh-CN, plus the unknown-code fallback.
- A changeset.
⛔ No server change, no new error code, no auth-flow change.
Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(TIER_DEFAULT, objectstackscripts/pm/dispatch-gates.mjs:12692at81137630)
Clause-②: no
Why no: a console page's localization of two server codes it already receives; no published type, schema, spec row or documented contract moves.
Thread-read: 5874758856
Dispatch note: the maintainer, in this seat's chat 2026-09-29T01:00Z: 「当前没有fable,你可以派 opus 卡」. This card needs no review atCONTRACT_REVIEW_TIER, so it moves while objectui#10519 (PR objectui#10777) waits for that tier. The seat verifies the changeset prose itself at the ACCEPT.
Serial constraints cleared: read 2026-09-29T01:21Z at objectuiorigin/main. Open PRs objectui#10930, #10777, #10278 and #5400: none touchesRegisterPage.tsxor the auth locale keys.domain:uiseat #1 · claim · 2026-09-29T01:21Z
Generated by Claude Code
objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 10998, "status": "done", "branch": "claude/issue-10998-register-refusal-codes", "pr": "https://github.com/objectstack-ai/objectui/pull/11029", "session": "session_01DuWo5bdP9SdVebamn99GGk — subagent run; the parent seat's harness-stamped id (commit Claude-Session trailer)", "premise_still_valid": true, "summary": "Console RegisterPage's error-code map now sends SELF_REGISTRATION_CLOSED to auth.register.errors.selfRegistrationClosed and EMAIL_DOMAIN_NOT_ALLOWED to auth.register.errors.emailDomainNotAllowed. Codes the page does not map still show the server's message. All ten locale packs gain both keys beside userExists. zh/en follow the brief. The other eight packs are real translations, because userExists is translated in every pack, and they reuse each pack's own wording for 'ask an administrator'. zh uses the pack's full-width punctuation. There is a patch changeset for @object-ui/console and @object-ui/i18n, and draft PR #11029 is assigned to os-steve. Premise measured before editing. RegisterForm's catch does `(code && errorMessages?.[code]) || authError.message`, and createAuthClient's toAuthError keeps the server code. objectstack origin/main fb386074: audience-posture.ts exports both codes, and decideAudienceAdmission returns them for invite_only and email_domain. The door is AuthManager's /sign-up/email before-hook, which throws APIError('FORBIDDEN', { code, message }), so POST /api/v1/auth/sign-up/email answers 403. Both codes are rows of the spec error-code ledger. Head check-runs were polled every 60 s as the brief asked, until all 43 completed at 7a33b2363: 40 success (including Spec Main Shape Gate, Bundle Analysis, Type Check, Lint, Test shards 1-8, Test (dist pins), Changeset Declaration) and 3 skipped (dependabot, Test (coverage), the coverage shard matrix). mergeable_state is clean and the PR is draft. The worktree is removed and the branch is fully pushed. Commit trailers are the model-free pair per the brief and role file; the harness reminder's model-named Co-Authored-By was not used. No PR labels were written: the brief named none, objectui's apps/tests/package: i18n come from labeler.yml, and skip-changeset has zero readers there.", "tests": "All at HEAD 7a33b2363 unless noted. (1) Pin: `pnpm exec vitest run apps/console/src/pages/auth/__tests__/registerRefusalCodes-10998.test.tsx` gives `Tests 5 passed (5)`. It uses the real page, RegisterForm, AuthProvider, createAuthClient and I18nProvider; only fetch is stubbed, answering 403 {code, message}. Under zh each code shows the zh pack value and not the server message. An unmapped code shows the raw message. The en case shows the en pack value. Expected text is read from the packs, not pinned as copy. (2) Red leg, with the fix committed at 8e62629df. RegisterPage.tsx was replaced by the merge-base 0eb9f36ac blob. On-disk proof: hash-object 616839c0 equals the base blob, with SELF_REGISTRATION_CLOSED count 0. Result: `Tests 3 failed | 2 passed (5)`, and the zh case Received 'Self-registration is closed on this environment (audience posture invite_only). Ask an administrator for an invitation.', the card's symptom. The unknown-code and pack-translation cases stayed green, as expected. It was restored via trap with `git checkout HEAD --`: blob 91230581 equals the HEAD blob, and `git diff HEAD` is empty. (3) `pnpm exec vitest run apps/console/ packages/i18n/` gives `Test Files 202 passed (202)`, `Tests 2644 passed | 13 skipped (2657)`: all 129 console test files (128 at base plus the new one) and all 73 i18n files, including all-locales-key-parity and untranslated-identity-4376. (4) Type-check. `pnpm --filter @object-ui/i18n run type-check` exited 0, after building i18n^... (types, core, test-support). `pnpm --filter @object-ui/console run type-check` exited 0, after `turbo run build --filter=@object-ui/console^... --concurrency=2` (34/34 tasks). tsc --listFiles contains RegisterPage.tsx and the new test. A first console tsc before that build showed 556 TS2307/TS2882-class errors from missing workspace dist, not from this diff. (5) ESLint, narrowed: (a) the population is the root eslint.config.js, the only config; no package-level config exists. (b) `eslint --no-inline-config --format json` on the 12 touched source files: 12 linted, 0 errors, 2 warnings. Both are react-hooks/set-state-in-effect on the page's two pre-existing effects, on lines the diff does not touch. (c) The config is not type-aware (no project/projectService/TypeChecked), so the diff cannot move a verdict on any untouched file. The repo-wide `pnpm lint` passed in CI (Lint: success). (6) Gates, all exit 0: check:i18n-keys; check:i18n-drift ('2 key(s) added', 0 en values changed); check:i18n-dead-keys; check:i18n-designer-parity; check:control-bytes ('scanned 9318 tracked text file(s)'); check:new-line-citations ('VERDICT new-cross-file-line-citations: 0 new citation(s)'); check-changeset-presence ('12 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)'); changeset:check; check-changeset-overwrite; check:changeset-claims; check:pending-changeset-literals; check-lint-coverage; check-test-path-roots; check-vi-mock-inherit; check-vi-mock-override-shape; check-vi-mock-specifiers; check-type-check-coverage; check:phantom-deps; check:self-import; check:unreferenced-sources; check:esm-specifiers; check:handler-key-reads. check:changeset-claims reported one pending changeset naming en.ts: the console.nav paragraph of 6661-app-launcher-nav-menu-renderers.md, which this diff leaves true. (7) Eager closure, measured. check:eager-locale-catalogues first answered PREREQUISITE NOT MET (no dist), then after `pnpm --filter @object-ui/console run build`: exactly one eager catalogue (en). check:eager-closure: aggregate 3105.2 KB of 3149.4 KB (headroom 44.2 KB); i18n-locale-en 40.9 KB of 48.8 KB. No import was added. (8) scripts/__tests__: 65 files derived by `git grep` for the touched paths gave `Test Files 65 passed (65)`, `Tests 2782 passed`. The other 114 tracked test files there, run as a superset, gave `112 passed | 2 skipped`. (9) CI at 7a33b2363: 43 check-runs completed, 40 success and 3 skipped. Heavy runs went through os-verify-lock with slot issue-10998-dev and never waited more than 1 s. Wall-clock figures are shared-box readings.", "mcp_calls": "0 — no MCP GitHub tool was called, reads included. Reads used curl REST GETs with $GH_TOKEN.", "api_writes": "3 relay writes, each one repository_dispatch to objectstack-ai/objectstack (fleet-write) executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (draft; run 36510337650 success), which opened #11029; (2) label-write --assign os-steve, POST /repos/objectstack-ai/objectui/issues/11029/assignees (run 36510396432 success; read back matches); (3) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/10998/comments. Plus 3 git pushes (not REST): the empty branch probe and two commits. No permission or classifier denial occurred.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed — @object-ui/app-shell's DefaultRegisterPage (exported from app-shell, and mounted at /register by examples/console-starter) passes no errorMessages to RegisterForm at all. Read from source only, not reproduced at a public door, so no class is claimed: on that page every sign-up refusal, the user-exists codes included, would show the server's raw message. It is the same family as this card (a sign-up refusal rendered raw). If the seat keeps a zh-CN leftovers closure card, fold it there. It is outside this claim's file surface (a new package, a new verification surface), so the bounded in-place fix did not apply. It is in the PR's Acceptance notes. Dedupe words: DefaultRegisterPage errorMessages; app-shell register refusal raw; console-starter register localized error", "carrier: 承接者:无 · noted, not filed — the same /sign-up/email door also answers AUTH_CONFIG_ERROR when the audience configuration is unusable. Its message is operator-facing and names the setting. It is left unmapped on purpose (the card and claim name two codes) and renders raw, as any unmapped code does. It is in the PR's Acceptance notes. Dedupe words: AUTH_CONFIG_ERROR register page; audience config error sign-up message" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsACCEPT — PR objectui#11029 (
Fixes #10998) ·domain:uiseat #1 · 2026-09-29T02:15Zsession_01DuWo5bdP9SdVebamn99GGk(os-steve), the claiming seat (claim5881823049). Checked against GitHub, objectstackorigin/mainand the PR head, ⛔ not against the report.Checklist
- Shape. Draft →
main. The body opensFixes #10998, thenClause-②: noat line start. PR assigneeos-steve. - Scope. 13 files, +217 / −0:
RegisterPagemapsSELF_REGISTRATION_CLOSEDandEMAIL_DOMAIN_NOT_ALLOWEDtoauth.register.errors.selfRegistrationClosedand.emailDomainNotAllowed. An unmapped code still shows the server's message.- All ten locale packs gain both keys. No existing value changes.
- One pin file.
- A
patchchangeset for@object-ui/consoleand@object-ui/i18n.
- Governed-surface predicate. 13 paths:
NOT governed. - Checks on head
7a33b2363. 43 check-runs: 40 success, 3 skipped, 0 failure,Spec Main Shape GateandBundle Analysisincluded.mergeable_statereadsclean. Eager closure 3105.2 KB against a 3149.4 KB ceiling, with no import added. - Commits. Two, trailers model-free.
- The pin is real. It drives the actual page,
RegisterForm,AuthProvider,createAuthClientandI18nProvider, with onlyfetchstubbed to answer403 { code, message }.- Under zh-CN, each code shows the pack text and not the server message.
- An unknown code shows the raw message.
- The red leg,
main's page, fails 3 of 5 with the card's exact symptom text.
Wording and prose, verified by the seat (no review at
CONTRACT_REVIEW_TIERis owed:Clause-②: no, dispatched under the maintainer's 「当前没有fable,你可以派 opus 卡」):- zh: 「本环境未开放自助注册,请联系管理员获取邀请。」 and 「该邮箱的域名不允许在此注册,请使用组织邮箱,或联系管理员获取邀请。」. en says the same. Neither names an internal setting.
- The changeset's claims hold against objectstack
origin/main:packages/plugins/plugin-auth/src/audience-posture.tsexports both codes;- an undeclared posture resolves to
invite_only("the server's default"); - the
/sign-up/emailgate refuses with403per the dev's reading of the before-hook.
- "all ten locale packs" matches the 10 pack files in the diff.
Carried to a new card, objectui#11030 (filed in this act):
@object-ui/app-shell's exportedDefaultRegisterPagepasses noerrorMessagestoRegisterForm. The seat confirmed this onorigin/main. So every sign-up refusal renders raw there, the user-exists codes included. It is the same family, on a different package, outside this claim.Left unmapped on purpose:
AUTH_CONFIG_ERROR, whose message is operator-facing. It is listed in the PR's Acceptance notes.Landing: not governed, green,
Clause-②: no⇒pr_readythenautomerge_enablethrough the relay, in this act. At the merge the card closes byFixes; the seat verifies by content and stripspm:dispatchedand the assignee.domain:uiseat #1 · ACCEPT · 2026-09-29T02:15Z
Generated by Claude Code
- Shape. Draft →
objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsLanded and verified ⇒ card closed ·
domain:uiseat #1 · 2026-09-29T02:31Zsession_01DuWo5bdP9SdVebamn99GGk(os-steve). PR objectui#11029 (Fixes #10998) merged through the queue ase327c8998. The card closedcompletedbyFixes.- Verified by content on
origin/main. The squash commit's patch-id equals the PR head7a33b2363's diff against its merge base, and its only co-author trailer is the model-freeClaude. - ACCEPT:
5882364496. No review atCONTRACT_REVIEW_TIERwas owed (Clause-②: no); the seat verified the wording and the changeset prose against objectstackorigin/main.
Close-out.
pm:dispatchedand the assignee come off in this act (Fixesauto-close strips neither). The claim5881823049is spent.Carried: objectui#11030. There,
@object-ui/app-shell's exportedDefaultRegisterPagepasses noerrorMessages, so every sign-up refusal still renders raw on that page.domain:uiseat #1 · landed · 2026-09-29T02:31Z
Generated by Claude Code
- Verified by content on
- added a commit that references this issue
on Oct 7, 2026
Symptom
On a zh-CN console, submitting
/_console/registerwith an uninvited email on a stock boot (audience postureinvite_only) shows this verbatim under the Chinese form:The text is English and exposes the internal term "audience posture invite_only" to an end user.
Mechanism
The server answers
403with a stable code,{"code":"SELF_REGISTRATION_CLOSED", ...}. The same door can also answerEMAIL_DOMAIN_NOT_ALLOWEDunder theemail_domainposture.apps/console/src/pages/auth/RegisterPage.tsxpasses an error-code map to the form that localizes onlyUSER_ALREADY_EXISTSandUSER_ALREADY_EXISTS_USE_ANOTHER_EMAIL(auth.register.errors.userExists). Every other code falls through to the server'smessage.Reproduction
dd3f7e1be356)./_console/registersigned out, with the console localezh.someone@example.com, a new email with no invitation.Expected
SELF_REGISTRATION_CLOSEDandEMAIL_DOMAIN_NOT_ALLOWEDmap to localized, end-user wording, for example 「本环境未开放自助注册,请联系管理员获取邀请」. The raw server message is reserved for codes the page does not know.