Skip to content

fix(app-shell): the exported register page says why a sign-up is refused, in the reader's language (objectui#11030) - #11052

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11030-register-refusal-map
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11030-register-refusal-map

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11030
Clause-②: yes
Why yes (copied from the claim): the shared map is a new export of the published @object-ui/app-shell, which widens its public surface.

What changed

DefaultRegisterPage, the register page @object-ui/app-shell publishes (mounted at /register by examples/console-starter), rendered RegisterForm with no errorMessages. So RegisterForm's catch ((code && errorMessages?.[code]) || authError.message) showed the server's own English message for every refused sign-up, in every language. For SELF_REGISTRATION_CLOSED that message also names the internal setting "audience posture invite_only".

Triage (comment 5882764311) ruled one map, used by both pages, and no second hand-written copy. This PR does that:

  • New export signUpRefusalMessages(t) in packages/app-shell/src/console/auth/signUpRefusalMessages.ts, re-exported from the package entry beside DefaultRegisterPage. It returns the refusal code to pack text map, keyed on the pack keys PR objectui#11029 added: auth.register.errors.userExists, .selfRegistrationClosed and .emailDomainNotAllowed. The parameter is typed with the one TranslateFn authority from @object-ui/i18n, as that type's header asks of a message builder. useObjectTranslation()'s t passes without a cast (both type-checks below are green).
  • DefaultRegisterPage passes errorMessages={signUpRefusalMessages(t)}.
  • The console app's RegisterPage passes the same call instead of its own inline object. It is the same code set, byte for byte: USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL, USER_ALREADY_EXISTS, SELF_REGISTRATION_CLOSED, EMAIL_DOMAIN_NOT_ALLOWED, with the same keys and the same inline defaultValue strings. apps/console already depended on @object-ui/app-shell (workspace:*), and App.tsx / main.tsx already import the barrel eagerly, so no dependency was added.
  • The unknown-code fallback is unchanged. AUTH_CONFIG_ERROR stays unmapped on purpose (operator-facing), as the docblock says.
  • Changeset .changeset/11030-register-refusal-map.md: '@object-ui/app-shell': patch, '@object-ui/console': patch (both are in the release group, and the console source changed even though what it shows did not).

Why the name is signUpRefusalMessages and not a register... name: in this codebase a register prefix names a registration function (registerAppComponent, registerBuiltInLocales). registerRefusalMessages would read as "register some messages". The name follows the endpoint (/sign-up/email) instead. The seat can rename it before landing if it prefers.

Premises checked on origin/main (63ab76112) before any edit

  1. DefaultRegisterPage renders RegisterForm with no errorMessages: confirmed.
  2. The console page mapped exactly the four codes above: confirmed. The shared map carries that set and nothing more.
  3. apps/console/package.json lists "@object-ui/app-shell": "workspace:*": confirmed.

Tests, all at c209a6fc5 (the only commit on this branch)

  • New pin packages/app-shell/src/console/auth/__tests__/registerRefusalCodes-11030.test.tsx. It renders the exported page under zh with the real form, a real AuthProvider over a real createAuthClient, and a real I18nProvider; only fetch is stubbed. Cases: one per mapped code (the three pack keys, with both user-exists codes), plus the unknown-code fallback, plus a check that the zh pack differs from en for all three keys. Expected text is read from the packs, not copied.
  • 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). The console page's objectui#10998 pin is unchanged and green. It is the control showing that the shared map keeps the console behaviour.
  • Ablation (through objectstack's scripts/ablation-replace.mjs, which checks the change on disk and restores to HEAD). Deleting errorMessages={signUpRefusalMessages(t)} from DefaultRegisterPage gave anchor 1 -> 0, blob a03ba581ab2f -> 1cd1ced129f3, then Tests 4 failed | 2 passed (6). The four code cases went red, for example Expected: "本环境未开放自助注册,请联系管理员获取邀请。" / Received: "Self-registration is closed on this environment (audience posture invite_only). ...". The pack check and the fallback stayed green, which is the expected direction. After restore, the blob equals HEAD and git diff HEAD is empty.
  • 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. After restore, the blob equals HEAD. So the console type-check reads the rebuilt packages/app-shell/dist/index.d.ts, which carries the export (built this run; there was no dist before it).
  • pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json) exits 0. tsc -p tsconfig.test.json --listFiles lists the new test file once and signUpRefusalMessages.ts once, so the test is inside what that project compiles.
  • pnpm --filter @object-ui/console type-check exits 0. The dependency closure was built first (turbo run build --filter="@object-ui/console^..." --concurrency=2: 34/34).
  • Tests that read the app-shell barrel: scripts/__tests__/one-authority-per-exported-name-6273.test.ts, scripts/__tests__/vite-declared-lazy-views.test.ts, rootLandingHasOneResolver-10042, appSidebarRemoved-5817 and expressionUser.mountSites.ratchet gave Test Files 5 passed (5) / Tests 54 passed (54).
  • 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)"), node scripts/check-control-bytes.mjs (exit 0), node scripts/check-changeset-no-major.mjs (exit 0), pnpm check:i18n-keys (exit 0: "every literal inline defaultValue matches the value the pack serves"), and pnpm check:new-line-citations (exit 0: 0 new citation(s)).
  • eslint, narrowed to the changed files, measured as follows. (1) Population: the 5 changed .ts/.tsx files. Each is in the lint population; none came back ignored. (2) --format json gave 5 results, 0 errors and 2 warnings. Both warnings are react-hooks/set-state-in-effect on lines of the console page this diff does not touch (the hasBootstrapped latch and the org auto-select). (3) Invariance: eslint --print-config on the changed files shows no parserOptions.project or projectService, so type-aware linting is off and this diff cannot change any verdict on an untouched file.

Declared narrowings (CI owns these)

  • The full pnpm test farm and repo-wide pnpm lint.
  • check:eager-closure / the performance budget needs a full console build. The console now imports one small module from the barrel it already loads eagerly, and drops the same amount of inline code. NOT MEASURED locally, reason: it needs a full console vite build, which CI runs.

Acceptance notes

  • Same family, not fixed here (outside the claim's file surface): DefaultLoginPage in packages/app-shell/src/console/auth/LoginPage.tsx also passes no errorMessages. 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. So on the exported login page, a refused sign-in shows the server's own message. This is read from source only and has not been reproduced at a running door, so no card is filed. It goes to the seat through the report.
  • No README or guide page documents the Default* auth pages, so there are no docs to update.

Generated by Claude Code

…sed, in the reader's language (objectui#11030)

DefaultRegisterPage passed no errorMessages to RegisterForm, so every refused
sign-up showed the server's raw English message. The refusal-code map now lives
once in @object-ui/app-shell (signUpRefusalMessages), keyed on the pack keys
auth.register.errors.userExists / .selfRegistrationClosed /
.emailDomainNotAllowed; both the exported page and the console app's page pass
it. AUTH_CONFIG_ERROR stays unmapped; the unknown-code fallback is unchanged.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 2 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6238-object-payload-enabled.md

  • names packages/app-shell/src/index.ts → packages/app-shell/src/index.ts — edited by this change

    No published type changed. ObjectMetadataPayload is exported from its module but that module is not re-exported by packages/app-shell/src/index.ts, the package's only entry, so the removed enabled?: boolean was never on the published surface and no **/src/index.ts is touched. What consumers can observe is behaviour: MetadataService is reachable through the published useMetadataService() hook, both method signatures are unchanged (PromiseANGLE-BRACKETS(void)), and the HTTP request they issue changes from a PUT with a body to a DELETE.

.changeset/6559-expression-user-input-contract.md

  • names packages/app-shell/src/index.ts → packages/app-shell/src/index.ts — edited by this change

    WHAT BREAKS, AND FOR WHOM. This tightens a signature published from the package entry (packages/app-shell/src/index.ts), so an external caller that passes an unchecked or under-declared value stops compiling on upgrade. That is accepted (maintainer ruling 2026-08-27, option A): the only calls it refuses are calls that were never conformant with the contract the module already declared. It ships as minor, not major — objectui's major tracks @objectstack's, so its own breaking changes ship as a minor with the break written down (scripts/check-changeset-no-major.mjs). ⛔ Keeping unknown and ⛔ adding a second, wider entry point were both declined.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 30f912a0d (merge-base with origin/main): 5 file(s) changed outside .changeset/, read against 1727 pending declaration(s) that publish a body (2331 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3105.9 KB 3149.4 KB
Main entry chunk (gzip) 149.4 KB 350 KB
Entry file index-BAWGbU7Z.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.80KB 6.24KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.92KB 133.97KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.99KB 63.22KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.02KB 14.87KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 165.60KB 42.64KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.59KB 22.47KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 19.75KB 6.81KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.30KB 6.99KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c209a6fc587d91bac37a890897fbcfb72b62ade2
Local-runs: none

Inputs: card #11030 (body, triage ruling 5882764311, claim 5883803546, os-dev-report 5884579830), PR #11052 (body, 6-file list, net diff against main), and the head's check-runs (filter=latest, read after every run completed: 43 runs, 40 success, 3 skipped — the two coverage lanes and dependabot — 0 failures). Files at the head were read through the contents API for context only.

① Derived judgments

  1. Public surface widens by one export — signUpRefusalMessages(t: TranslateFn): Record of string to string, new module packages/app-shell/src/console/auth/signUpRefusalMessages.ts, added to the @object-ui/app-shell entry (packages/app-shell/src/index.ts, beside DefaultRegisterPage). Right. Triage ruled one map owned by @object-ui/app-shell and used by both pages; the console app is a separate package, so the map has to be exported. The parameter is typed with the one TranslateFn authority from @object-ui/i18n (its header asks exactly this of a message builder); useObjectTranslation()'s t is react-i18next's bound t, which satisfies that shape. The return shape equals RegisterForm's prop errorMessages?: Record of string to string in @object-ui/auth.
  2. DefaultRegisterPage now passes errorMessages={signUpRefusalMessages(t)}. Right — this is the card. The RegisterForm accept set does not move; its catch ((code && errorMessages?.[code]) || authError.message) is untouched, so a code the map does not name still shows the server message.
  3. The console RegisterPage drops its inline map and passes the same call. Right — the ruling's "no second hand-written copy". The set is byte-identical to what was removed: USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL, USER_ALREADY_EXISTS, SELF_REGISTRATION_CLOSED, EMAIL_DOMAIN_NOT_ALLOWED, on auth.register.errors.userExists / .selfRegistrationClosed / .emailDomainNotAllowed with the same inline defaultValue strings, which match the en pack verbatim (check:i18n-keys green). apps/console already declares @object-ui/app-shell: workspace:* and App.tsx / main.tsx already import the barrel, so no dependency edge and no eager-closure growth is introduced (Bundle Analysis, the job that runs check-eager-closure-budget.mjs, is green on the head).
  4. AUTH_CONFIG_ERROR stays unmapped; no locale-pack key is added. Right — both keys sets exist in zh and en at the head; no packages/i18n/src/locales/* file is in the diff.
  5. Pin packages/app-shell/src/console/auth/__tests__/registerRefusalCodes-11030.test.tsx: the exported page under zh, one case per mapped code (4, covering all three pack keys), the unknown-code fallback, and a zh-differs-from-en guard; expected text read from the packs. Right and as ruled. It sits beside the existing authPages.socialLabels-10900.test.tsx (same render stack, same collection), is inside tsconfig.test.json's src/**/*.test.tsx, and the console's registerRefusalCodes-10998 pin is unchanged as the control. Test (shard 1..8/8), Test (dist pins), Type Check and Lint are green on the head.
  6. Changeset package set — @object-ui/app-shell and @object-ui/console. Right: both are in the fixed group of .changeset/config.json, apps/console/src changed, and check-changeset-presence.mjs guards fixed-group src/** (Changeset Declaration, Fixed Group Check, Claim Re-read, Overwrite Report all green). The level is judged in ②.
  7. Nothing else in the diff touches an accept set or a published surface. The claim's file surface is respected; the barrel edit is the export the claim's own Why yes names.

② Semver level

  • Clause-②: yes on the PR (and on the claim). Right — item ①.1 is a purely additive widening of a published package's public surface.
  • .changeset/11030-register-refusal-map.md grades '@object-ui/app-shell': patch. Wrong. A Clause-②: yes takes at least minor: that is the fleet's one reader of the declaration line (scripts/pm/clause2-line.mjs — "yes / yes (widening) … both take at least minor") and the Post-Task changeset rule it executes ("a bug fix in a released package takes patch UNLESS the PR declares Clause-②: yes, in which case at least minor"). objectui's own pending changesets grade widenings minor the same way (e.g. 5522-runtime-telemetry-consumer, 4795-bindable-text-keys). The head's green Changeset Bump Policy does not answer this: objectui's check-changeset-no-major.mjs has no level axis and refuses major only. The claim's patch is the dispatching seat's own conclusion and is not an input here.
  • The '@object-ui/console': patch entry is consistent with what the console publishes (no widening; the fixed group takes the highest level in any case).
  • Fix: change the one line to '@object-ui/app-shell': minor. Nothing else in the changeset needs to move; the body already states the new export and the unchanged fallback.

③ Boundary flags

  • D1 — name signUpRefusalMessages, not a register… name: answered, accepted. The claim fixed no identifier; a register prefix names a registration verb in this codebase (registerBuiltInLocales is imported by the pin itself). No rename asked.
  • D2 — changeset also lists @object-ui/console: patch: answered, accepted — ①.6. The level of the app-shell entry is the ② finding, not this flag.
  • D3 — pins render under zh, the card says zh-CN: answered, accepted. zh is the pack key in builtInLocales; the 10998 control pin uses the same spelling.
  • D4 — harness attribution trailer vs AGENTS.md: reported, not re-judged: the commit message is outside this record's inputs; the PR body ends with the session-URL footer AGENTS.md asks for.
  • Declared narrowings (pnpm test farm, repo-wide pnpm lint, check:eager-closure): all answered by the head's check-runs — Test (shard 1..8/8), Lint, Bundle Analysis green.
  • open_questions: none declared; none found.
  • Out-of-scope finding (DefaultLoginPage passes no errorMessages to LoginForm; carrier 承接者:无): escalated to the seat — file it as its own card (read-from-source, no reach class claimed, dedupe words are in the report) or record on 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 why not. Not a condition of this PR.

Findings for the FAIL: one — ② the @object-ui/app-shell changeset level (patch → minor). Everything in ① and ③ holds on this head; a head that carries the one-line fix re-enters at this tier with the same inputs.

Implemented-by: claude/issue-11030-register-refusal-map
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: FAIL


Generated by Claude Code

…1030)

The diff adds a public export to @object-ui/app-shell (signUpRefusalMessages)
and the claim declares Clause-② yes, which takes at least minor.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3106.0 KB 3149.4 KB
Main entry chunk (gzip) 149.4 KB 350 KB
Entry file index-BAMASgBe.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.80KB 6.24KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.92KB 133.97KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.99KB 63.22KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.02KB 14.87KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.54KB 61.80KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 165.60KB 42.64KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.75KB 22.52KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 19.75KB 6.81KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.30KB 6.99KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 788cae30dbd0981a9faa0c632e127bd0c9fb1d83
Local-runs: none

Second record on this PR, after the FAIL record 5884718105 on c209a6fc5. Inputs: card #11030 (body, triage ruling 5882764311, claim 5883803546, the two os-dev-reports 5884579830 and 5884751451), PR #11052 (body, 6-file list, net diff against main at 30f912a0d), and the head's check-runs (filter=latest, read after every run completed: 43 runs, 40 success, 3 skipped — the two coverage lanes and dependabot — 0 failures, none pending). Files at the head were read through the contents API for context only. The whole net diff was judged again, not only the patch round.

What moved since c209a6fc5: three commits on the branch — the original, a merge of main (232cdf9b2, two parents), and 788cae30d, which edits one line of .changeset/11030-register-refusal-map.md. The net diff against main is byte-identical to the one reviewed in 5884718105 except that line ('@object-ui/app-shell': patch → minor); the merge brings nothing of its own into the net diff, and the 6-file list, +204 / -22, is unchanged.

① Derived judgments

  1. Public surface widens by one export — signUpRefusalMessages(t: TranslateFn): Record of string to string, new module packages/app-shell/src/console/auth/signUpRefusalMessages.ts, added to the @object-ui/app-shell entry (packages/app-shell/src/index.ts, beside DefaultRegisterPage). Right. Triage ruled one map owned by @object-ui/app-shell and used by both pages; the console app is a separate package, so the map has to be exported. The parameter is typed with the one TranslateFn authority from @object-ui/i18n (its header asks exactly this of a message builder); useObjectTranslation()'s t is react-i18next's bound t, which satisfies that shape. The return shape equals RegisterForm's prop errorMessages?: Record of string to string in @object-ui/auth.
  2. DefaultRegisterPage now passes errorMessages={signUpRefusalMessages(t)}. Right — this is the card. The RegisterForm accept set does not move; its catch ((code && errorMessages?.[code]) || authError.message) is untouched, so a code the map does not name still shows the server message.
  3. The console RegisterPage drops its inline map and passes the same call. Right — the ruling's "no second hand-written copy". The set is byte-identical to what was removed: USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL, USER_ALREADY_EXISTS, SELF_REGISTRATION_CLOSED, EMAIL_DOMAIN_NOT_ALLOWED, on auth.register.errors.userExists / .selfRegistrationClosed / .emailDomainNotAllowed with the same inline defaultValue strings, which match the en pack verbatim. apps/console already declares @object-ui/app-shell: workspace:* and App.tsx / main.tsx already import the barrel, so no dependency edge and no eager-closure growth is introduced (Bundle Analysis, the job that runs check-eager-closure-budget.mjs, is green on the head).
  4. AUTH_CONFIG_ERROR stays unmapped; no locale-pack key is added. Right — the three keys exist in zh and en at the head; no packages/i18n/src/locales/* file is in the diff.
  5. Pin packages/app-shell/src/console/auth/__tests__/registerRefusalCodes-11030.test.tsx: the exported page under zh, one case per mapped code (4, covering all three pack keys), the unknown-code fallback, and a zh-differs-from-en guard; expected text read from the packs. Right and as ruled. It sits beside the existing authPages.socialLabels-10900.test.tsx (same render stack, same collection), is inside tsconfig.test.json's src/**/*.test.tsx, and the console's registerRefusalCodes-10998 pin is unchanged as the control. Test (shard 1..8/8), Test, Test (dist pins), Type Check and Lint are green on the head.
  6. Changeset package set — @object-ui/app-shell and @object-ui/console. Right: both are in the fixed group of .changeset/config.json, apps/console/src changed, and check-changeset-presence.mjs guards fixed-group src/** (Changeset Declaration, Fixed Group Check, Claim Re-read, Overwrite Report, Bump Policy all green on the head).
  7. The merge of main (232cdf9b2) adds nothing to the net diff (item above); the two pending changesets whose bodies name packages/app-shell/src/index.ts — 6238-object-payload-enabled.md ("ObjectMetadataPayload … not re-exported by packages/app-shell/src/index.ts") and 6559-expression-user-input-contract.md (a tightened signature published from the entry) — stay true, because this diff adds only the signUpRefusalMessages re-export and touches neither name. Right, read at the head.
  8. Nothing else in the diff touches an accept set or a published surface. The claim's file surface is respected; the barrel edit is the export the claim's own Why yes names.

② Semver level

  • Clause-②: yes on the PR (and on the claim). Right — item ①.1 is a purely additive widening of a published package's public surface.
  • .changeset/11030-register-refusal-map.md at the head grades '@object-ui/app-shell': minor, '@object-ui/console': patch. Right, and the 5884718105 finding is closed. A Clause-②: yes takes at least minor (the fleet's one reader of the line, scripts/pm/clause2-line.mjs, and the Post-Task changeset rule it executes); the console entry publishes no widening, and the fixed group takes the highest level in any case. The body is unchanged and names no bump level, so it does not contradict its own front matter (Changeset Claim Re-read green). No major anywhere (Changeset Bump Policy green).
  • Level and declaration now match what the diff publishes.

③ Boundary flags

  • D1 — name signUpRefusalMessages, not a register… name: answered, accepted (unchanged from 5884718105). The claim fixed no identifier; a register prefix names a registration verb in this codebase. No rename asked.
  • D2 — changeset also lists @object-ui/console: patch: answered, accepted — ①.6.
  • D3 — pins render under zh, the card says zh-CN: answered, accepted. zh is the pack key in builtInLocales; the 10998 control pin uses the same spelling.
  • D4 — harness attribution trailer vs AGENTS.md: reported, not re-judged: commit messages are outside this record's inputs; the PR body ends with the session-URL footer AGENTS.md asks for.
  • Round-2 narrowings (5884751451): "vitest / type-check at 788cae3 NOT MEASURED" — answered by the head's check-runs (Test (shard 1..8/8), Test (dist pins), Type Check and Lint all green); "pnpm changeset status not run" — accepted, no workflow invokes it and Changeset Fixed Group Check is the repo's front-matter gate, green on the head; the two flagged pending changesets — answered in ①.7.
  • Declared narrowings from round 1 (pnpm test farm, repo-wide pnpm lint, check:eager-closure): answered by the head's check-runs, same names.
  • open_questions: none declared in either report; none found.
  • PR body prose is stale in two places — the "What changed" bullet still reads '@object-ui/app-shell': patch, and the tests heading still says c209a6fc5 is "the only commit on this branch". Prose only: no gate reads either line, and the changeset and the declaration line are what this record judges. Escalated to the seat to amend the body before landing (an issue-body edit, no new head needed). Not a finding on this head.
  • Out-of-scope finding (DefaultLoginPage passes no errorMessages to LoginForm; carrier 承接者:无): still escalated to the seat — file it as its own card or record on 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 why not; no card appears on the card's thread yet. Not a condition of this PR.

Implemented-by: claude/issue-11030-register-refusal-map
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 06:54
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 180a34a Sep 29, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11030-register-refusal-map branch September 29, 2026 07:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment