Skip to content

fix(types): declare a form field's avatarField; avatar_field stays refused as its second spelling (objectui#12130) - #12158

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12130-avatar-field
Oct 11, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12130-avatar-field

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Part of #12130
Clause-②: yes (widening): declaring avatarField widens the strict authoring face's accept set by one key, and its value check narrows the tolerant face, where the key used to be stripped unjudged.

Round 2 of objectui#12130: declare avatarField on the form-field face, and keep avatar_field refused as its second spelling. This PR leaves the card open. step and className stay OPEN in the census for round 3.

Implemented by the os-dev dispatched from the domain:spec @ objectui seat, session https://claude.ai/code/session_01V8qYZseSLfKH3V8tPu2f73, claim comment 6112572465. Base: main at 0665c3b (round 1, PR objectui#12154).

What changed

1. The premise, measured first (commit 06d7231, before any declaration; examples/schema-catalog/test/form-field-channel-effects-12130.test.tsx). For user and for lookup, a field with picker: 'search' is rendered through the real SchemaRenderer, form renderer and @object-ui/fields registrations. The candidate row carries an avatar in image and another in photo. Without the key, the search picker's person row draws image. With avatarField: 'photo', it draws photo. Both legs read a drawn img, so neither leg passes on a picker that draws no avatar. happy-dom never loads an image, so the case reports images as loaded, the technique of PeoplePicker.outputFls-10433.test.tsx in @object-ui/fields.

2. The declaration (packages/types/src/form.ts FormField, packages/types/src/zod/form.zod.ts FormFieldSchema, in parity):

key how declared read by (census) measured effect
avatarField as the widgets read it: string (default image) lookup, master_detail, user the record field each search-picker candidate draws its avatar from

The notes beside both declarations now say that avatar_field stays undeclared, as the second spelling (AGENTS.md #0.1).

3. The census (form-field-channel-census-12130.test.tsx). The avatarField ledger row leaves OPEN. DECLARED_BY_12130 gains avatarField: ['lookup', 'master_detail', 'user'], which the census re-derives on every run. The avatar_field row moves to the second-spelling class beside allow_create, lookup_columns, lookup_page_size and reference_field. Its readers, LookupField and UserField, are unchanged.

4. The face pin (packages/types/src/__tests__/form-field-channel-keys-12130.test.ts). avatarField parses on the strict face, and a misspelled sibling is still refused at the field. The tolerant face keeps the value. A non-string value is refused at that key on both faces. avatar_field is refused by name. The TypeScript member is typed, not the index signature's any, and equals string | undefined. form-field-zod-coverage.test.ts's declared-key list gains the key.

5. One .changeset/12130-avatar-field.md (@object-ui/types: minor, with the breaking semantics stated, Clause-②: yes (widening)). Patch 1 (5b7f24d, claim amendment 6113007901) also corrects round 1 pending .changeset/12130-form-field-channel-keys.md: its 「Still undeclared, and listed as open in the census」 list drops avatarField / avatar_field and now reads step and className. Prose only; front matter untouched.

Readings per ruling and hypothesis

  • Premise (Ruling 3): holds. The readers on 0665c3b, quoted: fieldMeta?.avatar_field ?? fieldMeta?.avatarField ?? 'image' in LookupField and avatar_field: meta?.avatar_field ?? meta?.avatarField ?? 'image' in UserField. Both read avatarField when avatar_field is absent. The value changes what the search picker draws, as measured in item 1 for each reader.
  • H1 confirmed. The read-by types are lookup, master_detail and user. The census's exact-equality row asserts this. master_detail renders through the lookup widget, so the reader attribution is still LookupField / UserField.
  • H2 confirmed, with one addition. The installed spec (@objectstack/spec 17.7.0) has a FieldSchema of 75 keys that includes neither spelling (control: lookupPageSize reads present). So the type is objectui's own (string), like defaultName. The addition: the spec names camelCase avatarField once, in BULK_PARAM_WIDGET_CONFIG_KEYS, the refusal guidance list of BulkActionParamSchema. That list does not name avatar_field. So where the spec spells this widget-config key, it spells it camelCase.
  • H3 confirmed. The ledger's OPEN rows are exactly step and className. Both are untouched.

Acceptance notes

  • Round 1 changeset, corrected in this PR. .changeset/12130-form-field-channel-keys.md (pending) said 「Still undeclared, and listed as open in the census: step, …; avatarField / avatar_field; and className.」, which this PR makes false for avatarField. Patch 1 (5b7f24d, claim amendment 6113007901) removes 「avatarField / avatar_field;」 from that list and drops the round-2 changeset then-redundant 「Supersedes」 bullet. check-changeset-overwrite reports the modified file (report-only, exit 0).
  • When the key draws anything. LookupField reads avatarField on every lookup / user / master_detail render, but the value is drawn only by the search picker (picker: 'search', which is the user widget's default) and by its selected-value chips. The chips fall back to image when the named field is empty. With the classic quick-select, the key is accepted and has no visible effect. This is the same bound as subtitle.
  • Docs. content/docs/fields/user.mdx still teaches avatar_field: 'image', which the strict face refuses. That page, UserFieldMetadata.avatar_field and the snake-first reads belong to objectui#12157, the snake_case retirement. None is touched here.
  • Spec guidance text (noted, not filed). The prescription of BULK_PARAM_WIDGET_CONFIG_KEYS calls its keys 「keys of a FIELD (FieldSchema, data/field.zod.ts)」. Several keys on the list are not FieldSchema keys: avatarField, picker, subtitle, idField, crop, capture, defaultName. The refusal it gives for a bulk param is still right, so this is only an observation.

Verification (code at 2083a81; patch 1 5b7f24d changes changeset prose only)

  • Runtime suites, under the shared verify lock, from the repo root: vitest run packages/types/ gave 369 files and 10181 tests passed. vitest run examples/schema-catalog/ gave 45 files and 2343 passed. vitest run scripts/__tests__/ gave 180 files passed and 2 skipped (5489 tests passed).
  • Type-check: pnpm --filter @object-ui/types type-check and pnpm --filter @object-ui/example-schema-catalog type-check both exit 0, after the schema-catalog dependency closure was built (28 of 28 turbo tasks). tsc -p tsconfig.test.json --listFiles lists both touched test files in each test project. A probe also compiled two literals: FormField with avatarField: 'photo' compiles, and with avatarField: 3 it is TS2322.
  • Public door: pnpm --filter '@object-ui/cli...' build exits 0, then pnpm check exits 0 with 「286 validated, 147 recognised but not validated, 0 did not validate」. objectui validate (the built CLI) answers as follows. A user field with avatarField: "photo" is valid (exit 0). The same document with avatar_field fails with 「Unrecognized key: "avatar_field"」 (exit 1). With avatarField: 3 it fails with 「expected string, received number」 (exit 1).
  • Gates, each exit 0: check:element-data-source-declaration, check:designer-field-key-parity, check:spec-symbols, check:phantom-deps, check:control-bytes, and check:new-line-citations (「0 new citation(s)」). Derived and added: check-test-path-roots, check-vi-mock-override-shape, check-lint-coverage, check-type-check-coverage, check:unreferenced-sources and check:pending-changeset-literals. The doc gates over the built types also each exit 0, after turbo run build over the check-doc-snippet-types --build-filter set (35 of 35 tasks): check-doc-snippet-types, check-doc-example-types, check-doc-json-fences (「Every judged JSON node document on the scan surface passes the strict authoring face.」), check-doc-component-types, check-prompt-component-keys, check:doc-fences and check:doc-example-ids.
  • Changeset gates at 5b7f24d, each exit 0: check-changeset-presence (「this change declares 1 changeset(s): .changeset/12130-avatar-field.md」), -claims (「No pending changeset names a file this change touches.」), -no-major, -overwrite (report-only: 「1 changeset(s) added, 1 modified, 0 deleted」, naming .changeset/12130-form-field-channel-keys.md, @object-ui/types: minor at base and now; 「Report-only: this gate does not fail the build on the findings above.」), -fixed, check:pending-changeset-literals and check:control-bytes.
  • Targeted eslint (--no-inline-config --format json) on the 6 changed .ts / .tsx files: 6 files, 0 errors, 9 warnings. All 9 are no-explicit-any in form.ts, and the BASE blob of form.ts reads 9 too. The population is eslint.config.js's **/*.{ts,tsx} minus its global ignores. No block of that config sets parserOptions, so type-aware linting is off and this diff cannot move a verdict on an untouched file.
  • Ablation, from the committed state. Each leg went through the anchor-verified ablation-replace.mjs with its own restore trap plus a shell trap. Afterwards, the blob hash equals HEAD's and git diff HEAD is empty.
    • Leg 1: avatarField renamed in FormFieldSchema. Anchor x1 to x0, blob f45d49bc3b9d to f1f30566b77f. The census goes red with 2 failed: unexpected: ["avatarField ← entry@fields/src/widgets/LookupField.tsx, entry@fields/src/widgets/UserField.tsx"], and the avatarField row of DECLARED_BY_12130. The census reads @object-ui/types/zod through the vitest alias to src, so no build sits between the mutation and the run.
    • Leg 2: FormField.avatarField widened to any. Blob 6c094d6866dc to ee6088169f1e. tsc -p tsconfig.test.json exits 2 with two TS2344 errors, at the face pin's assertionChannelMembersAreTyped and assertionLocalMembers.
  • Full farm and the Spec Main Shape Gate are CI's.

Generated by Claude Code

…icker, before declaring it (objectui#12130)

A `user` or `lookup` field with `picker: 'search'` draws each candidate's
avatar from `image` without the key, and from the named record field with
`avatarField`. Both legs read a drawn `img` (images reported as loaded),
so neither passes on a picker that draws no avatar.

Claude-Session: https://claude.ai/code/session_01V8qYZseSLfKH3V8tPu2f73
Co-authored-by: Claude <noreply@anthropic.com>
… refused as its second spelling (objectui#12130)

The `lookup`, `master_detail` and `user` widgets read `avatarField` off a
form's `fields[]` entry, and with `picker: 'search'` each candidate draws
its avatar from that record field (the effects pin committed before this).
The strict authoring face refused it. `FormField` and `FormFieldSchema`
now declare it in parity, typed as the widgets read it (`string`): the
spec's `FieldSchema` declares neither spelling.

`avatar_field`, which the widgets read first, stays undeclared and moves
to the census ledger's second-spelling class (AGENTS.md #0.1). The census
ledger's OPEN rows are now `step` and `className`.

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

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

✅ Nothing to re-read

No pending changeset names a file this change touches, no address in this pull request's own prose points at a line this change moves, and no changeset here negates a package it declares. An earlier revision did. That request to re-read does not apply to the current diff.

This comment is updated in place on every re-run rather than posted again, so the thread does not grow one per push.

Compared the checked-out tree with 8cc7158b4 (merge-base with origin/main): 6 file(s) changed outside .changeset/, read against 329 pending declaration(s) that publish a body (336 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 291 chunks) 3177.5 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-C2gY776P.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) 20.05KB 7.41KB
app-shell (runtime-config.js) 22.59KB 7.89KB
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.15KB 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) 41.19KB 11.12KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 587.77KB 141.53KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 275.50KB 70.15KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
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.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
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) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
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.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 250.37KB 66.07KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.80KB 46.04KB
plugin-gantt (index.js) 179.37KB 45.17KB
plugin-grid (index.js) 255.19KB 70.94KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 122.82KB 31.24KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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

…ist, and the now-redundant Supersedes bullet (objectui#12130)

Round 1's pending changeset listed `avatarField` / `avatar_field` as still
undeclared and open; this PR declares `avatarField`, so the sentence is
corrected in the same PR (claim amendment 6113007901). The list now reads
`step` and `className`. The round-2 changeset's Supersedes bullet existed
only to cover that sentence and is dropped. Prose only; front matter
untouched in both files.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 291 chunks) 3177.5 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-C2gY776P.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) 20.05KB 7.41KB
app-shell (runtime-config.js) 22.59KB 7.89KB
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.15KB 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) 41.19KB 11.12KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 587.77KB 141.53KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 275.50KB 70.15KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
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.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
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) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
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.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 250.37KB 66.07KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.80KB 46.04KB
plugin-gantt (index.js) 179.37KB 45.17KB
plugin-grid (index.js) 255.19KB 70.94KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 122.82KB 31.24KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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: 5b7f24d409dae0103e91cc22f7fd49e2ad293405
Local-runs: none

Read: card #12130 (body and all 11 comments, among them the round-2 claim 6112572465, its amendment 6113007901 and the two os-dev-reports 6112988153 and 6113039732), PR #12158 (body, the 8-file list, the net diff from merge-base 0665c3b to this head) and the check-runs on this head. The widget reads were taken off origin/main with git show, not from the dev's quotation. Nothing was built, run or re-run.

① Derived judgments

  • Strict authoring face (StrictAnyComponentSchema, the objectui validate / objectui check door) gains fields[].avatarField as z.string().optional(), accepted on every field type because the face is flat: RIGHT. The reads exist on main: LookupField reads fieldMeta?.avatar_field ?? fieldMeta?.avatarField ?? 'image', and UserField normalises avatar_field: meta?.avatar_field ?? meta?.avatarField ?? 'image' before spreading into LookupField; the value reaches PersonRow and the selection chips through PeoplePicker's avatarField prop. The effect is pinned before the declaration (commit 06d7231 precedes 2083a81): with and without the key, for user and for lookup with picker: 'search', reading a drawn img under [data-testid="person-row"] behind the LoadedImage technique that the cited PeoplePicker.outputFls-10433.test.tsx uses; the trigger selector matches LookupField's lookup-trigger- testid. This is triage 6106872887's direction (declare each key the widget reads) and the seat answer in claim 6112572465 (camelCase).
  • Tolerant face (AnyComponentSchema / safeValidateSchema) now keeps a string avatarField and refuses a non-string one: RIGHT, the shape round 1 landed for its fourteen keys. The narrowing reaches the authoring doors only; no renderer calls the zod faces (the round-1 ACCEPT 6112343855 reading, which this diff does not move).
  • TypeScript face, FormField.avatarField?: string: RIGHT. Typed as the widgets read it. The spec's FieldSchema (17.7.0) declares neither spelling, checked in the spec source: BULK_PARAM_WIDGET_CONFIG_KEYS names camelCase avatarField and no avatar_field, and field.zod.ts carries no avatar key. UserFieldMetadata declares only avatar_field, so by-reference typing would cement the spelling the ruling refuses. The face pin asserts Equal to string | undefined and IsAny false.
  • avatar_field stays refused: RIGHT, by the face's generic undeclared-key refusal naming the key (fields.0.avatar_field), not by a dedicated alias arm like the grid's ?: never tombstones from objectui#11610. That is the reading the round-1 ACCEPT made explicit for the other four second spellings; AGENTS.md #0.1 and the seat answer govern.
  • Census: avatarField leaves the OPEN rows into DECLARED_BY_12130 as ['lookup', 'master_detail', 'user'], and avatar_field moves to the ALIAS class with readers LookupField / UserField unchanged: RIGHT. master_detail resolves to field:master_detail in field-type-alias.ts, served by the lookup widget, so the reader attribution holds. The exact-equality rows keep the pin bidirectional, and OPEN is exactly step and className, as the claim deferred to round 3.
  • Zod-coverage list gains avatarField: RIGHT, required by its exact-shape assertion against FormFieldSchema.shape.
  • No renderer, no UserFieldMetadata, no docs touched: RIGHT inside the claim's ⛔ list (objectui#12157's). relationalMetaKeys.ts's avatarField: no-producer verdict is about the spec FieldSchema, which this diff does not change, so it stays true.
  • Public surface of other packages: none. The 8 files are four under packages/types/src, two schema-catalog tests and two changesets; CI Changeset Declaration reads 1 released package.

② Semver level

  • .changeset/12130-avatar-field.md, @object-ui/types: minor with the BREAKING semantics stated (a non-string avatarField no longer validates on the tolerant face): RIGHT under the repo's version-alignment rule (no major; a breaking change ships as minor with the semantics in the body). Changeset Bump Policy, Changeset Fixed Group Check, Changeset Declaration and Changeset Claim Re-read read success on this head.
  • The prose-only correction of the pending round-1 .changeset/12130-form-field-channel-keys.md (only avatarField / avatar_field leaves its 「Still undeclared」 list; front matter @object-ui/types: minor unchanged): RIGHT, per amendment 6113007901 option A. Changeset Overwrite Report is report-only and reads success. The 「Supersedes」 bullet is gone from the new changeset, as the amendment asked.
  • Clause-②: line. The value yes agrees across the claim (yes), the PR body (yes (widening), at line start on its second line) and the changeset (yes (widening)): RIGHT. The arm is the one under-spelled reading on this head: the PR body's own sentence states a strict-face widening AND a tolerant-face narrowing, and clause2-line.mjs's table spells that combination yes (narrowing) (「a diff that widens one surface and narrows another. Both facts are true and both are read」), with yes (widening) reserved for yes said out loud. Both arms take at least minor, the changeset body states the narrowing where the release notes read it, and I found no scripts/pm reader that acts on the arm (said per AGENTS.md 完善设计器的每一个细节 #9; not re-derived by a gate), so the level does not move and the record does not fail on it. The seat may re-spell the PR body line when it next edits it.

③ Boundary flags

  • Round-2 report 6112988153, OQ1 (the round-1 changeset is now false for avatarField): ANSWERED by amendment 6113007901 (option A) and applied at this head; verified in the diff.
  • Round-2 deviation, the check-changeset-claims flag: ANSWERED by the same patch; CI Changeset Claim Re-read reads success.
  • Round-2 deviation, ablation leg 1 run as a rename: ANSWERED. For the census the mutation is the same (the read key becomes undeclared). Not re-run here.
  • Patch-1 report 6113039732, deviation 「PR body not corrected by me」 with four pr_body_corrections: ANSWERED. The PR body as read carries all four (item 5's Patch 1 sentence; the 「Round 1 changeset, corrected in this PR」 bullet; the Verification heading 「code at 2083a81; patch 1 5b7f24d changes changeset prose only」; the 「Changeset gates at 5b7f24d」 bullet) and keeps the session-URL footer.
  • Patch-1 deviation, base 0665c3b while main is at 8cc7158 (mergeable_state: behind): ANSWERED. The two newer main commits (fix(app-shell): a position decision output lists the position registry and commits position names (objectui#7611) #12152, app-shell; feat(fields,app-shell): the capability select lists the capability registry; the matrix badge reads the layered envelope (part of objectui#7611) #12153, CapabilityMultiSelectField, app-shell, i18n) touch neither packages/types, nor the schema-catalog tests, nor LookupField / UserField, nor either changeset. The queue merges against main; no contract surface moves.
  • Both reports, commit trailers in the model-free pair: ANSWERED. That is AGENTS.md's own commit-message rule, which the harness's attribution reminder yields to; the three commits on this head carry it. Not a deviation.
  • Claim premise (「the dev verifies it before any declaration」): HELD. Measured first (06d7231), declared second (2083a81); premise_still_valid: true agrees with the source read above.
  • Out-of-scope finding, the spec's BULK_PARAM_WIDGET_CONFIG_KEYS wording (「keys of a FIELD」 for keys FieldSchema lacks): ESCALATED to the seat's filing gate as objectstack's. Verified in the spec source; the refusal it drives is right, so nothing at a door is wrong.
  • Out-of-scope finding, content/docs/fields/user.mdx teaches avatar_field: 'image', which the strict face refuses, and no doc teaches avatarField: ESCALATED. Carried by objectui#12157 per the claim's ⛔ list; until it lands the public doc contradicts the public door for this key. Sequencing is the seat's.
  • Check-runs on this head at the reading 2026-10-11T20:00Z: 32 success, 3 skipped by design (two coverage placeholders and dependabot), 7 in progress (Test shards 2, 4, 5, 6, 7 and 8 of 8, and Spec Main Shape Gate), 0 failure. Lint, Type Check, Build & E2E, Build Docs, Doc Snippet Type Check, Line Citation Gate, Governed Surface Queue Guard and every changeset check read success. Convergence is the seat's landing check, not this verdict.

Implemented-by: claude/issue-12130-avatar-field
Reviewed-by: session_01V8qYZseSLfKH3V8tPu2f73

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 11, 2026 20:09
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 11, 2026 20:09
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit c0c2f79 Oct 11, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12130-avatar-field branch October 11, 2026 20:28
zhuangjianguo pushed a commit that referenced this pull request Oct 11, 2026
Brings main to faf4ce9 (objectui#12151, #12152, #12153, #12154, #12148,
#12149, #12147, #12158, #12159); none of them touches this branch's files.

Claude-Session: https://claude.ai/code/session_01TYgwmFK1q4KJ6Qq2WRLzsD
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants