Skip to content

refactor(types,fields,app-shell): field metadata stops declaring host-written and field-level regex keys; geolocation leaves the union (objectui#12242) - #12293

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-12242-field-metadata-kept-positions
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-12242-field-metadata-kept-positions

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #12242
Clause-②: yes

Round two of the field metadata family close-out. It executes maintainer ruling A on the card (comment 6135661503: Q1 A, Q2 A, Q3 B), from main 74964ad6. The authoring face of a field now describes only what a published object can carry. One position is held, not executed: dueLike, because the ruling's fork clause fired on it (see "The fork" below). That is why the first line is Part of, not Fixes: the card stays open for that one position.

What changed

  • @object-ui/types (field-types.ts, form.ts):
    • Q1, moved off the authoring face. BaseFieldMetadata.mobile_fullscreen, LookupFieldMetadata.idField, and UserFieldMetadata.picker / subtitle are no longer declared. Each spot carries a "there is deliberately no X here" note that names where the key lives now.
    • FormField keeps idField / picker / subtitle at the same types. They were declared by reference to the removed members; the entry now declares them itself (string, 'search' | 'default', string[]). The ruling keeps the faces that already typed them.
    • Q2, never tombstones. TextFieldMetadata.pattern and pattern_message, and BaseFieldMetadata.validate, are now never tombstones. Each docblock names the spec's object-level format validation rule, { type: 'format', name, field, regex, message }. FieldValidationFunction and FieldConstraints are marked @deprecated because no member declares them now, the same treatment round one gave VisibilityCondition.
    • Q3, out of the union. GeolocationFieldMetadata leaves FieldMetadata. The interface stays exported, marked @deprecated, and the field:geolocation widget registration is untouched.
    • FieldValidationRules.pattern's docblock no longer says buildValidationRules compiles a string pattern.
  • @object-ui/fields:
    • buildValidationRules no longer reads pattern / pattern_message / validate. A location field's rule is now the stored-value check alone. No client pre-check of the object-level rule is added (the ruling did not take B).
    • New package-local hand-off: LongTextCarrierHandoff (widgets/longTextCarrierHandoff.ts, not exported from the index, following the renderHandoff.ts / chartRenderHandoff.ts pattern). TextAreaField and RichTextField read mobile_fullscreen through it, intersected into their existing declared casts. Its producer is still ObjectForm, unchanged.
    • LookupField reads idField / picker / subtitle through an annotated alias, typed as Pick of FormField over those three keys. FormField is the existing face that declares them. The reads were untyped (any) before; behaviour is unchanged.
  • @object-ui/app-shell, FlowReferenceField (the flow designer's record-lookup cell). It builds LookupField's carrier with idField and the people-picker keys, taken from its own package-local RecordLookupBinding. The literal was typed as LookupFieldMetadata through the widget prop, so removing idField turned app-shell type-check red (TS2353). The literal is now typed as LookupFieldMetadata intersected with Pick of FormField over the three keys. This is the producer side of the hand-off, fixed on the producer.
  • @object-ui/plugin-grid (relationalMetaKeys.ts): the widget-contract verdict's docblock and the idField row's note now name FormField instead of LookupFieldMetadata. This is internal data, not exported, with no behaviour change. The derivation test's alias control now expects LookupField's second alias (pickerConfig).
  • Docs:
    • text.mdx teaches the object-level format rule, written as a prose list (see Acceptance notes).
    • lookup.mdx and user.mdx drop the moved keys from their field metadata snippets and say where idField / picker / subtitle live now. user.mdx shows them on a typed FormField.
    • blocks/forms.mdx, plugins/plugin-form.mdx and the plugin-form README no longer say a field-level string pattern is compiled.

The fork: dueLike is held

The ruling's premise for Q1 is that the spec refuses each key and no published object can carry it. For dueLike that is no longer true on @objectstack/spec's own main:

  • feat(spec,lint): a date or datetime field declares its deadline: dueLike and settledWhen objectstack#22503 (merged 2026-10-09, commit 215e66204, pending changeset 22227-field-deadline-due-like.md) declares dueLike and settledWhen on FieldSchema, for date / datetime only. Its changeset names objectui's date cells as the reader.
  • The spec installed here (17.7.0) still refuses dueLike (unrecognized_keys), so the adoption arrives with the next spec release.
  • Three example apps in objectstack write dueLike: true on published object definitions: app-crm's activity, app-showcase's task and app-todo's task.
  • Its readers take it off the object's field definition: resolveDueLike in @object-ui/fields, which the date and datetime cells call, and the record-detail enrichment copy. That is an authored key, which a runtime hand-off type cannot honestly carry.

So dueLike is a reader no hand-off type can carry, and the key is moving toward the authoring face, not away from it. Per the fork clause, this run reports it and does not decide it. DateFieldMetadata.dueLike, DateTimeFieldMetadata.dueLike, date.mdx, datetime.mdx, DetailViewFieldSchema.dueLike and schema-reference.md are all unchanged. No silent C.

Census of the kept rows (starting from PR objectui#12257's table)

Interface Member / value Installed FieldSchema (17.7.0) Spec main Non-test readers and producers This PR
Base mobile_fullscreen unrecognized_keys not declared produced by ObjectForm; read by TextAreaField, RichTextField, and the form renderer's built-in textarea branch (through its own props type) moved to LongTextCarrierHandoff
Base validate unrecognized_keys not declared buildValidationRules never; read removed
Text pattern, pattern_message unrecognized_keys object-level format rule only buildValidationRules never; reads removed
Date, DateTime dueLike unrecognized_keys declared (objectstack#22503) resolveDueLike (date and datetime cells), DetailViewFieldSchema.dueLike, the detail enrichment copy held (fork)
Lookup idField unrecognized_keys not on FieldSchema (only on InlineGridColumnSchema) LookupField; produced by FormField entries, action parameters, FlowReferenceField and GridField inline columns moved; FormField declares it
User picker, subtitle unrecognized_keys not declared LookupField, and UserField (defaults); produced by FormField entries and FlowReferenceField moved; FormField declares them
Geolocation type geolocation invalid_value at type not a FieldType the field:geolocation widget out of the union

field-metadata-family-retired-12242.test.ts re-derives the installed-spec column on every run, each row behind an accepting control. The other columns are a reading taken once.

Fork-clause measurements

  • type: 'geolocation' producers. The query was the regex type['"]?\s*:\s*['"]geolocation['"], excluding tests, specs, content/**, docs/**, markdown and changelogs:
    • objectui 74964ad6: 1 hit, GeolocationFieldMetadata's own type member. The control, the same query for text: 221 files.
    • objectstack origin/main c9b5b24b0: 0 hits; control: 80 files.
    • hotcrm 0f84c9c: 0 hits; control: 16 files.
  • Writers of the other Q1 keys in objectstack (packages/**, examples/**, apps/**) and hotcrm. Every hit was read:
    • mobile_fullscreen: 0 in both.
    • picker / subtitle: none on a field definition. The hits are related-list add.picker, page-header subtitle and chart subtitle.
    • idField: only InlineGridColumnSchema and unrelated identifiers.
    • dueLike: the fork above.

Export closure (Clause-②)

Measured on the built .d.ts: base 74964ad6 from a scratch worktree, head from this branch. The comparison is per top-level declaration, as a token multiset with comments excluded, so the type printer's union-member order cannot read as a change. @deprecated is compared separately.

  • Export names: identical for every exports entry of @object-ui/types, @object-ui/fields and @object-ui/plugin-grid. No name is added or removed.
  • @object-ui/types declarations changed:
    • BaseFieldMetadata, TextFieldMetadata, LookupFieldMetadata and UserFieldMetadata (the members above).
    • FieldMetadata (one union member fewer).
    • FormField: the same three member types, now spelled out. form-field-channel-keys-12130.test.ts pins them Equal to string / 'search' | 'default' / string[], each with undefined.
    • @deprecated added on FieldValidationFunction, FieldConstraints and GeolocationFieldMetadata.
  • @object-ui/fields: one added file, widgets/longTextCarrierHandoff.d.ts. No emitted declaration file references it, and no export entry reaches it. Every other declaration is unchanged.
  • @object-ui/plugin-grid: no declaration changed.

Tests and gates (at 31da2084, from the repo root; heavy runs under the shared verify lock)

  • type-check, with the script name echoed and after the dependency-closure build (turbo, 28 of 28 tasks), exit 0 for each of @object-ui/types, fields, core, components, plugin-form, plugin-detail, plugin-grid and app-shell. app-shell was red once (the FlowReferenceField literal above) and green after that fix. types was re-run at 31da2084: exit 0.
  • vitest run:
    • packages/types/, plus the two new fields pins: 375 files, 10239 tests passed (at 31da2084).
    • packages/fields/: 252 files passed (1 skipped), 4031 tests passed (at bf418ddc; no fields source changed after it).
    • A targeted set of 28 files, 354 tests passed (at bf418ddc): plugin-grid relationalMetaCopySet.derivation and lookupPickerKeys-7154; the plugin-form location suites and ObjectForm.mobileFullscreen; app-shell FlowReferenceField* and paramToField; the components fullscreen-textarea suites; schema-catalog form-field-channel-census-12130, form-field-channel-effects-12130 and form-builtin-branch-dom-allowlist-12155; plugin-detail fieldEnrichment.
    • scripts/__tests__/ (at bf418ddc): 186 files passed, 1 red: check-doc-component-types, "this repository is green", on this branch's first text.mdx. That page now teaches the rule in prose, and the re-run of that file is green (at 155bd4e1, with the types suite).
  • Gates, each exit 0 at 31da2084, with its own verdict line:
    • check:doc-types: "Every documented component type is registered".
    • check:doc-snippets: "Every covered documentation snippet compiles against the built types" (after the scoped build it names, 37 of 37 tasks).
    • check:doc-examples: "Every covered @example compiles, or fails exactly as its ledger row declares".
    • check:doc-json, check:doc-fences, check:doc-example-ids, and check-doc-links ("Links are valid across 17 scan roots").
    • check:readme-exports: OK, 0 fabricated.
    • check:spec-symbols, check:self-import, check:phantom-deps, check:unreferenced-sources, check:handler-key-reads, check:esm-specifiers (specifier leg), and check:designer-field-key-parity ("OK").
    • check:new-line-citations: "0 new citation(s)". check:control-bytes: OK.
    • check:changeset-claims: report-only. It names round one's types changeset, whose paragraph about the family pin stays true: this PR adds rows and changes none.
    • check:pending-changeset-literals.
    • check-changeset-presence: "20 source file(s) of 5 released package(s) changed, and this change declares 2 changeset(s)". Also check-changeset-no-major and check-changeset-fixed.
    • check:eager-closure: 3131.3 KB of 3204.6 KB.
    • check-governed-queue-guard --test over the 28 changed paths: NOT GOVERNED.
  • Targeted eslint on the 19 changed .ts / .tsx files: 0 errors and 233 warnings. The 16 files that exist at base carry the identical per-file counts there, and the 3 new files carry 0 of each.
    • Population: eslint.config.js's TS block (**/*.{ts,tsx}).
    • The count is read from --format json.
    • Invariance: the config enables no type-aware linting (no project / projectService), and no rule in eslint-rules/ reads another file. So this diff cannot move any untouched file's verdict.

Ablations (each from committed state, through objectstack's ablation-replace.mjs, wrap mode, under the lock)

Each mutation hit its anchor, and the blob changed. Each restore proved blob equal to HEAD and an empty git diff HEAD.

  • A1, pattern?: never to string | RegExp: tsc -p tsconfig.test.json red, TS2344 on the tombstone assertion and TS2578 (unused @ts-expect-error) on _pattern.
  • A2, idField?: string put back on LookupFieldMetadata: red, TS2578 on _idField and TS2344 on the moved-off assertion.
  • A3, GeolocationFieldMetadata put back in the union: red, TS2578 on _geolocation.
  • A4, buildValidationRules' field-level pattern read put back: 2 rows of the new fields pin red ("expected { pattern: … } to be undefined", "expected [ 'maxLength', 'pattern' ] to deeply equal [ 'maxLength' ]").
  • A5, the field-level validate read put back: 1 row red ("expected { validate: [Function validate] } to be undefined"). The location control row stayed green, as expected: that branch replaces rules.validate wholesale.

The first attempt at A2 to A5 was a no-op: each replacement contained its own anchor, so the tool refused it, and the command never ran. Those readings were discarded and re-run with replacements that consume the anchor.

Changesets

  • 12242-types-kept-positions.md: @object-ui/types minor, BREAKING, FROM → TO for every member and the union. Patch round 1 corrects the pending objectui#12130 changeset's bullet about these keys in place.
  • 12242-fields-validation-rules.md: @object-ui/fields minor, BREAKING for hosts that hand-write pattern / validate in code. Those hosts lose the client-side check; the server enforces the object's format rule on save.
  • plugin-grid (internal note text) and app-shell (the same carrier object, now built in a typed const before the render branch) carry no behaviour change and get no release entry; the presence gate counts their files under these two declarations.

Acceptance notes (observed, not filed)

  • text.mdx teaches the format rule as a prose list, not a code fence. check:doc-types reads every fenced type value as a node type. Its per-file ledger (where round one exempted object.mdx's json_schema) is outside this claim's file surface, and prose is outside that gate's question.
  • ENRICHED_FIELD_METADATA_KEYS (plugin-detail) still copies idField, picker, subtitle and dueLike off the served object definition. The installed spec refuses all four on a field, so for spec-compliant data those copies reach nothing. The ruling says behaviour is unchanged, so they stay. Carrier: none.
  • No in-repo path emits the form renderer's validation.pattern default message any more; buildValidationRules was its only producer. check:i18n-dead-keys (report-only, exit 0) already listed the pack key as needs-review before this change. Carrier: none.
  • buildValidationRules still reads required_message, min_length_message, max_length_message, min_message and max_message, which no type declares. The spec's answer on them was not measured here. Carrier: none.
  • GeolocationFieldMetadata stays exported, marked @deprecated, rather than removed. That follows the VisibilityCondition precedent, and the barrel (index.ts) is outside this claim's file surface. Removing it would be a separate decision.

Generated by Claude Code

…nd field-level regex keys

Ruling A on objectui#12242, round two. The authoring face of a field
describes only what a published object can carry.

- mobile_fullscreen, idField, picker and subtitle leave the field
  metadata types. mobile_fullscreen is typed on LongTextCarrierHandoff
  in @object-ui/fields; the three picker keys are declared on FormField,
  which LookupField reads them through.
- pattern, pattern_message and validate retire as never tombstones, and
  buildValidationRules stops reading them. text.mdx teaches the object's
  format validation rule.
- GeolocationFieldMetadata leaves the FieldMetadata union; the
  field:geolocation widget stays registered.
- dueLike is held: @objectstack/spec main declares it on FieldSchema.

Claude-Session: https://claude.ai/code/session_015YrgVw7e65cs1ndbebt46G
Co-authored-by: Claude <noreply@anthropic.com>
…er on the hand-off, plus changesets

FlowReferenceField builds LookupField's carrier with idField and the
people-picker keys; it now types that literal as LookupFieldMetadata
plus FormField's picker keys, since the field metadata types no longer
declare them. buildValidationRules gains a pin for the retired
field-level regex keys. Changesets for @object-ui/types and
@object-ui/fields.

Claude-Session: https://claude.ai/code/session_015YrgVw7e65cs1ndbebt46G
Co-authored-by: Claude <noreply@anthropic.com>
…l two read-site quotes

The text page's format-rule example moves out of a code fence: the
doc-types gate reads fenced `type` values as node types, and a
validation rule type is not one. The 6140 read-site pin quotes the two
long-text casts as they read now, with the carrier hand-off intersected.

Claude-Session: https://claude.ai/code/session_015YrgVw7e65cs1ndbebt46G
Co-authored-by: Claude <noreply@anthropic.com>
The FormField picker keys no longer reference UserFieldMetadata, so the
import served only a {@link}, which object-ui/no-unused-imports refuses.

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

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 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/12242-types-field-metadata-family.md

  • names field-metadata-family-retired-12242.test.ts → packages/types/src/__tests__/field-metadata-family-retired-12242.test.ts — edited by this change

    TypeScript that writes a retired member, or type: 'auto_number' / type: 'object', on an annotated field metadata literal no longer compiles. For visible_on, write visibleWhen with a CEL predicate. For currency, write currencyConfig: { currencyMode: 'fixed', defaultCurrency }. Otherwise drop the key. The spec refuses every one of these keys and values by name, so a field written with them could not be published anyway. field-metadata-family-retired-12242.test.ts pins each member and both type values, beside the installed spec's own answer. (The bump is minor by this repo's release model: objectui's major follows the @objectstack family major, and its own breaking changes ship as minor with the breaking semantics stated here.)

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.

Compared the checked-out tree with 45ae6b89e (merge-base with origin/main): 26 file(s) changed outside .changeset/, read against 399 pending declaration(s) that publish a body (413 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 72 chunks) 3131.3 KB 3204.6 KB
Main entry chunk (gzip) 73.5 KB 350 KB
Entry file index-DpRVHUwk.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.69KB 7.62KB
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.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
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) 588.51KB 141.71KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 244.49KB 68.27KB
fields (index.js) 279.76KB 71.33KB
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) 143.63KB 38.79KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 254.49KB 67.01KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 178.28KB 46.22KB
plugin-gantt (index.js) 178.63KB 45.19KB
plugin-grid (index.js) 255.12KB 70.93KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 123.35KB 31.54KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 45.65KB 12.81KB
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.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
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

…er keys are declared now

objectui#12242 moved idField / picker / subtitle off the field metadata
types, so FormField declares them itself. The pending 12130 bullet said
"by reference to the field metadata types"; it now says what is true.
The 12242 types changeset drops its "supersedes" sentence, which the
corrected note no longer needs.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 72 chunks) 3131.3 KB 3204.6 KB
Main entry chunk (gzip) 73.5 KB 350 KB
Entry file index-DpRVHUwk.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.69KB 7.62KB
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.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
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) 588.51KB 141.71KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 244.49KB 68.27KB
fields (index.js) 279.76KB 71.33KB
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) 143.63KB 38.79KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 254.49KB 67.01KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 178.28KB 46.22KB
plugin-gantt (index.js) 178.63KB 45.19KB
plugin-grid (index.js) 255.12KB 70.93KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 123.35KB 31.54KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 45.65KB 12.81KB
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.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
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: d6f28b48bff2e8f513981e646b8b6a2e9f0898c1
Local-runs: none

Read at 2026-10-13T02:44Z · PR objectui#12293 (draft, base main, head repo = base repo) against card objectui#12242 · ruling 6135661503 (letter A: Q1 A, Q2 A, Q3 B) · claim 6135868241 · dev report 6136825207 · seat decision box 6136874685. Inputs: the card body and all 11 comments, the PR body, its 29-file list, the net diff against main at the merge-base (29 files, +646/−283, which is what the API serves and what git diff merge-base..head on a fetch of the branch gives), and the head's check-runs. The head is one commit on top of a merge of main; the net diff was judged, not the merge commit. Nothing was built, run or re-run.

① Derived judgments

Every accept-set and public-surface change the diff implies, each judged:

  1. BaseFieldMetadata.mobile_fullscreen leaves the authoring face — RIGHT. Ruling A Q1. The installed spec refuses it by name, re-derived on every run by the new it.each row of field-metadata-family-retired-12242.test.ts with an accepting control per row. Its consumers (TextAreaField, RichTextField) now read it through LongTextCarrierHandoff, a package-local interface in @object-ui/fields that is not reachable from the package index (verified by git grep on the head: no reference from packages/fields/src/index.tsx), following the renderHandoff.ts / chartRenderHandoff.ts precedent (both exist on the head). Its one producer, ObjectForm, is unchanged (a spread into the carrier). Caveat, stated not hidden: producer and consumers live in different packages, so the producer side is not typed against the hand-off; it never was, and the behaviour pin longTextCarrierHandoff-12242.test.tsx (stamped carrier draws the toggle, plain carrier does not; the hand-off member is a non-any optional boolean; the authoring face refuses the key on a fresh literal) plus the kept ObjectForm.mobileFullscreen suite hold the seam. No new as any.

  2. LookupFieldMetadata.idField, UserFieldMetadata.picker and .subtitle leave the authoring face — RIGHT. Ruling A Q1: "the FormField and DetailViewField faces that already type them do not move." FormField keeps all three, now declared by its own types (string, 'search' | 'default', string[]), pinned Equal in form-field-channel-keys-12130.test.ts; the resolved types are what the removed members resolved to, so FormField's public shape does not move. LookupField reads the three through pickerConfig, a Pick of FormField, where before it read them off an any. FlowReferenceField (app-shell), the one typed in-repo producer, types its carrier as LookupFieldMetadata intersected with that Pick — fixed on the producer, no cast; app-shell type-check was red (TS2353) without it, so the file is in scope.

  3. TextFieldMetadata.pattern / pattern_message and BaseFieldMetadata.validate become never tombstones — RIGHT. Ruling A Q2 and triage's never convention (6123790917). Each docblock names the spec's object-level format rule. buildValidationRules drops the three reads; on the head no non-test reader of field.pattern / pattern_message remains (verified by git grep), and the location branch emits the stored-value check alone. The new pin buildValidationRules.fieldLevelRegexRetired-12242.test.ts carries controls (the email / url format rule, maxLength, the location validator). The dev's ablations A1, A4, A5 went red on reversal. FieldValidationFunction and FieldConstraints stay exported, @deprecated: no export removed, narrowing only. The family pin's new ObjectSchema row shows the object parses with the format rule and refuses the field-level pattern.

  4. GeolocationFieldMetadata leaves the FieldMetadata union; the interface stays exported @deprecated; field:geolocation stays registered — RIGHT. Ruling A Q3 says exactly "leaves the union; the widget registration stays"; the barrel still exports the interface (verified on the head). The remaining 'geolocation' literals in non-test source are string lists, registry keys and an editType compare, none a discriminant compare on a FieldMetadata-typed value, so no TS2367 is implied. _geolocation pins the refusal with _location as the accepting control.

  5. dueLike held on both date types, both docs pages, DetailViewFieldSchema and schema-reference.md — RIGHT under the ruling's fork clause. The spec's own main declares dueLike on FieldSchema (objectstack#22503), unpublished at the installed 17.7.0; three objectstack example apps author it on published objects; its readers take it off the object's field definition, which no runtime hand-off type can honestly carry. The dev reported and stopped on that one position, no silent C, and the PR's first line is Part of, so the card stays open for it.

  6. Docs follow the verdicts — RIGHT. text.mdx teaches the object-level format rule in prose (no fenced type: value, so check:doc-types is asked no new question and its ledger is untouched); lookup.mdx and user.mdx drop the moved keys from the *FieldMetadata snippets and show the picker keys on a typed FormField; blocks/forms.mdx, plugins/plugin-form.mdx and the plugin-form README no longer say a string pattern is compiled.

  7. Pending changeset 12130-form-field-channel-keys.md corrected in place (patch round 1, the head commit) — RIGHT. Its bullet said the three picker keys were declared "by reference to the field metadata types"; it now says they are declared on FormField itself with the three types, which is what the diff does. The seat amended the claim surface for this file (6136874685, precedent objectui#12157); Changeset Overwrite Report records the correction.

  8. plugin-grid relationalMetaKeys.ts — internal, no surface change. Docblock and note text only; the module is not exported from the plugin-grid index (verified on the head). The derivation test's alias control now expects pickerConfig beside fieldMeta / cascadeMeta: a new fact about the seam, declared.

  9. Deleting field-metadata-mobile-fullscreen.test.ts — RIGHT. Its premise (the flag is declared on BaseFieldMetadata) is the one the ruling reverses, and the replacement pin names it and carries its assertions forward at the hand-off.

  10. Export closure. No export name added or removed on @object-ui/types, @object-ui/fields or @object-ui/plugin-grid; six @object-ui/types declarations narrow; three gain @deprecated; LongTextCarrierHandoff is not exported. This is the narrowing the claim's Clause-②: yes priced in.

Shape: none of the 29 paths is a governed surface (Governed Surface Queue Guard success; check-governed-queue-guard --test NOT GOVERNED per the dev); 929 changed lines; draft; PR assignee equals the card's; no needs:contract-review label; auto-merge not armed.

② Semver level

  • @object-ui/types: minor, BREAKING, FROM → TO for every moved member, every tombstone and the union, with the one-line fixes (move the picker keys to the form entry or action parameter; declare a format rule on the object; use location; drop mobile_fullscreen). Matches the diff. minor is the fixed group's spelling for a breaking change (check-changeset-no-major refuses major).
  • @object-ui/fields: minor, BREAKING for a host that hand-writes pattern / validate, FROM → TO on buildValidationRules; the type-only widget changes named. Matches the diff.
  • @object-ui/app-shell and @object-ui/plugin-grid: no behaviour change, no entry; the fixed group bumps them with the two above, and objectui's presence rule is declare-once (check-changeset-presence: 20 source files of 5 released packages, 2 changesets, passed).
  • Not skip-changeset: both named packages publish type and behaviour changes. Right.
  • Clause-②: line: Clause-②: yes at line start in the PR body and in the types changeset. yes owes at least minor: satisfied. No (narrowing) arm is written; the declaration allows at most one arm, so none is well-formed, and the body states the narrowing in words. The claim (6135868241) declared yes on the same ground.
  • objectui carries no ADR-0087 registration gate (grep of scripts/, workflows and package.json on main); that marker is not owed here.
  • Gate conclusions on the head: Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report, Changeset Claim Re-read all success. The re-read flagged round one's 12242-types-field-metadata-family.md naming the family test file; its paragraph ("pins each member and both type values, beside the installed spec's own answer") still holds, since rows were added and none changed.

③ Boundary flags

Dev deviations (report 6136825207), each answered or escalated:

  1. First line Part of #12242, not the dispatch's Fixes — answered: correct under the fork clause; the seat's decision box (6136874685) lands the PR as built with the card open on dueLike.
  2. dueLike held — escalated: open question A/B is in the decision box 6136874685, card labelled needs-user-decision; this PR leaves dueLike as main has it, so landing it decides nothing there and no gate on this head waits on it.
  3. Files beyond the claim's lead list (FlowReferenceField.tsx; form.ts / form.zod.ts; lookup.mdx, user.mdx, blocks/forms.mdx, plugins/plugin-form.mdx; six pins) — answered: each is inside a class the claim names (hand-off producers and readers; the pages that teach these keys; the pins that read them), and FlowReferenceField is the producer-side fix the ruling wants.
  4. GeolocationFieldMetadata kept exported @deprecated — answered: the ruling text is "leaves the union"; the barrel is outside the surface; round one's VisibilityCondition precedent was accepted.
  5. FieldValidationFunction / FieldConstraints kept @deprecated — answered: removing an export is a second decision; this diff narrows only.
  6. The 12130 pending changeset bullet made false — answered: corrected in patch round 1, in this head's net diff.
  7. No dispatch-gates.mjs in objectui — procedural, no bearing on the contract.
  8. Model-free commit trailers — answered: all six branch commits carry Claude-Session and Co-authored-by: Claude, no model identifier (read on the head); the repo's form outranks the harness reminder.
  9. Suites cited at the sha they ran on — answered: the head's check-runs are the gate reading of record (below).
  10. A heavy closure script killed and replaced by a token-multiset comparison — no bearing.

Out-of-scope findings (carrier: none), each answered:

  • ENRICHED_FIELD_METADATA_KEYS (plugin-detail) still copies idField, picker, subtitle and dueLike off the served object definition — a dead copy for spec-compliant data, kept because the ruling says behaviour is unchanged; it is a string list, not a typed read, so it compiles; the dueLike row waits on the dueLike decision. Not a FAIL reason.
  • The form renderer's validation.pattern default message now has no in-repo producer; check:i18n-dead-keys (report-only) already lists the pack key. Carrier none.
  • buildValidationRules still reads required_message, min_length_message, max_length_message, min_message, max_message, declared by no face, with the spec's answer unmeasured — the family card's class in the reverse direction (read but undeclared). Escalated to the seat for a filing decision; outside this PR's claim.

Reviewer's own observations:

  • UserField supplies its picker / subtitle defaults off a pre-existing props.field as any carrier, untouched by this PR; the reads reach LookupField through the typed pickerConfig. Not a regression; a de-cast candidate for the seat. Carrier none.
  • Check-runs on the head, read as the gate verdicts after they settled (polled from the REST API, not re-run): 33 check-runs, 33 completed, 30 success, 3 skipped by design (Test (coverage), the coverage shard matrix, dependabot), 0 red. Green by name: Lint, Type Check, Repo Gates, Governed Surface Queue Guard, Build & E2E, Build Docs, Spec Main Shape Gate, Test (dist pins), all 12 Test (shard n/12), Line Citation Gate, Bundle Analysis (eager closure 3131.3 KB of 3204.6 KB, PASS), Live E2E (informational), the five changeset gates named in ②, label. No check-run on this head was still in progress when this record was written.

Implemented-by: claude/issue-12242-field-metadata-kept-positions
Reviewed-by: session_015YrgVw7e65cs1ndbebt46G

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 13, 2026 02:47
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 13, 2026 02:47
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 5d3f437 Oct 13, 2026
34 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12242-field-metadata-kept-positions branch October 13, 2026 03:02
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