Skip to content

fix(fields,plugin-form): the served help leg retires, and fieldHelpText moves to @object-ui/fields as the one read (objectui#12213) - #12283

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-12213-help-leg-retire
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-12213-help-leg-retire

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12213
Clause-②: yes

What this changes

Ruling 6134063362 (letter A): the served help read leg retires now. A field's help line has ONE read, fieldHelpText, which now lives in @object-ui/fields, and both readers call it.

Reader FROM TO
Record form: ObjectForm's field builder and fromObjectSchema plugin-form's own fieldHelpText: field.inlineHelpText || field.help || field.description fieldHelpText from @object-ui/fields: field.inlineHelpText || field.description
resolveActionParam, for a field-backed param with no helpText of its own the same order written out by hand, help included param.helpText ?? fieldHelpText(field)
  • The two local help members are gone: plugin-form's FieldHelpTextSource goes with its copy of the function (plugin-form keeps no copy and no re-export), and RuntimeField.help leaves the resolver.
  • The empty-string rule is unchanged: inlineHelpText: '' still falls back to description (||, not ??).
  • Landing point: packages/fields (the read, its home file fieldHelpText.ts, the entry's export) and packages/plugin-form (the two callers), as the dispatch expected. packages/types/src/field-types.ts changes one docblock phrase only: fieldHelpText now lives in @object-ui/fields.
  • Server floor, stated in the changesets: translated field help needs an objectstack server on spec 17.7.0 or later (objectstack-ai/objectstack bab76850c, fix(spec): serve a field's translated help on description, never on an undeclared help objectstack#21956). Against an older server, a field's help line shows its stored description, untranslated.

Export closure, measured on the built declarations

A script built on the TypeScript checker lists getExportsOfModule of each package's exports['.'].types file, on a build of base 48fb558d and on a build of this head:

Package entry Base Head Difference
@object-ui/fields dist/index.d.ts 191 names, no fieldHelpText 192 names fieldHelpText only, typed (field: Pick of BaseFieldMetadata's "inlineHelpText" and "description") => string | undefined, declared in dist/fieldHelpText.d.ts, whose one import is BaseFieldMetadata from @object-ui/types
@object-ui/plugin-form dist/index.d.ts 59 names 59 names none: the two lists are identical

Controls in the same runs: resolveActionParam PRESENT in the fields entry, ObjectForm PRESENT in the plugin-form entry. FieldHelpTextSource is not an export name at either point. Its only built home was plugin-form's dist/sectionFields.d.ts, a file no exports entry reaches, which carried it and the old function at base and carries neither now (0 hits). (Generic type arguments are spelled out in words here because GitHub strips angle-bracket spans from bodies.)

No home both readers could reach without a public export exists: @object-ui/fields publishes . and ./style.css only, and plugin-form imports it through ..

The ruling's fork clause, measured (no other producer, so no stop)

objectui 48fb558d, outside tests and docs:

  • Readers of a .help member: the two retired here, plus non-field shapes only (the flow inspector's config fields in FlowNodeConfigField.tsx and StudioDesignSurface.tsx, ShareDialog options, and BulkActionDialog's BulkActionParam.help, which resolveBulkActions fills from the resolver's helpText).
  • Writers of a help: key: 17 files, none writing it onto a field definition (flow config fields, i18n catalogues, share options, bulk params). JSON "help": keys: 0 files (control "description": 118 files).
  • The client translation transform transformSpecTranslations flattens a field's label and options only. The console requests /api/v1/i18n/translations (control hit) and never getFieldLabels (0 hits repo-wide).

objectstack 84643802:

  • The field translator in packages/spec/src/system/i18n-resolver.ts: next.help = 0 hits, control next.description = translatedDescription 1 hit.
  • Every other help: hit outside tests and docs is a translation bundle entry (the translator's input), a spec alias table, the translation schema's own help member, a bulk-action param, or the /i18n/labels response (GetFieldLabelsResponse.labels.FIELD.help, built by resolveObjectFieldLabels). That last one is a separate labels document, not a field definition, and objectui never requests it, so it reaches neither reader.

Pins, flipped from "read" to "ignored"

The repo-wide sweep of tests for a served field help found exactly two pins, both flipped in this round. Each now asserts the new meaning, not just the absence of the old one. (field-metadata-inline-help-text-12213.test.ts in types pins help as a refused authoring key; that stays true and is unchanged.)

  • objectFormInlineHelpText-12213.test.tsx (plugin-form; flat, sectioned and tabbed): a field with help and description draws the description and not the help text. A new helpOnly field draws no help line (empty accessible description, text absent). inlineHelpText still wins over help.
  • ActionParamDialog.fieldHelpLine-12213.test.tsx (app-shell, real resolve and real dialog): the served field lends its description. A new helpOnly field lends no helpText, and the text is not on screen.
  • New: fieldHelpText-12213.test.tsx (fields) pins the entry export: the order, the empty-string rule, help ignored, and, at type level, that a typed literal carrying help does not compile.

Reverse verification (from committed head 06213286, restored and hash-checked)

Both mutations went through ablation-replace.mjs in wrap mode: anchor hit 1 to 0, blob changed, and after the run the blob equals the HEAD blob and git diff HEAD is empty. Vitest aliases @object-ui/fields to src, so no dist leg was needed.

  • A, runtime: fieldHelpText reads help again, between inlineHelpText and description. The three pin files went from 28 passed to 7 failed and 21 passed. The 7 are the "ignored" cases: 3 form layouts, 2 dialog cases, 2 fieldHelpText cases.
  • B, type level: the parameter type gains help again. The fields test type-check fails with TS2578: Unused '@ts-expect-error' directive in fieldHelpText-12213.test.tsx.

Gates (all at head 06213286)

  • Builds under the shared lock: the @object-ui/console... closure, 35 of 35 tasks; then the @object-ui/cli... and @object-ui/plugin-ai... closures, 10 of 10.
  • Type-check, each echoing its tsc chain, exit 0: @object-ui/types, @object-ui/fields, @object-ui/plugin-form, @object-ui/plugin-grid, @object-ui/app-shell.
  • Tests, exit 0: packages/fields/, packages/plugin-form/ and the types 12213 pin: 431 files passed, 1 skipped; 6226 tests passed, 8 skipped. The consumer set: 97 files re-derived by a grep of tests naming resolveActionParam, ActionParamDialog, fieldHelpText or inlineHelpText, plus three more from the dispatch's lead list and a wider helpText grep of tests (FormPage.richTextHints-12110, FormPage.fieldSpec, plugin-grid's resolveBulkActions): 100 files passed, 979 tests. scripts/__tests__/: 187 files passed, 2 skipped; 5621 tests passed.
  • Exit 0: check:readme-exports (592 self-imports, all real, 0 unbuilt; the new README import fieldHelpText is judged real), check:spec-symbols, check:self-import, check:phantom-deps, check:unreferenced-sources, check:handler-key-reads, check:designer-field-key-parity, check:eager-closure (a head reading only: 3131.3 KB gzipped against a 3204.6 KB budget, headroom 73.3 KB; the base was not measured), check:esm-specifiers, check:changeset-claims, check:pending-changeset-literals, check:new-line-citations (0 new), check:control-bytes, check-changeset-fixed, check-changeset-presence, check-changeset-no-major, check:test-path-roots, check:vi-mock-specifiers, check:dist-completeness, check-type-check-coverage. The governed-surface guard on the 13 paths answers NOT GOVERNED.
  • ESLint, a measured narrowing. Population: eslint.config.js lints **/*.{ts,tsx}. Run: eslint --no-inline-config --format json over the 9 touched ts/tsx files, which the JSON output counts as 9 files: 0 errors and 213 warnings. 212 of the warnings are on the base blobs of the same files. The one new warning is react-refresh/only-export-components on the new fieldHelpText re-export in packages/fields/src/index.tsx, the same warning the file's 53 other non-component exports carry. Invariance: no parserOptions.project or projectService (type-aware linting is off), and no rule in eslint-rules/ reads another file, so this diff cannot move a verdict on an untouched file.

Changesets

  • .changeset/12213-fields-field-help-text-export.md (new, @object-ui/fields minor): the new export, with the Clause-② line.
  • .changeset/12213-plugin-form-inline-help-text.md and .changeset/12213-fields-action-param-help.md are EDITED, patch to minor. Both are unreleased: the three CHANGELOGs carry 0 mentions of 12213. Each said the help leg is still read, which this change makes false within the same release. Each now states the change from the last release (FROM help first, TO inlineHelpText || description) and carries the BREAKING section with the server floor. check-changeset-overwrite (report-only) lists both as modified, its case 2: a deliberate correction.
  • @object-ui/types gets no new changeset: one docblock phrase in its shipped .d.ts, with no change to behaviour or surface. Its pending 12213-types-inline-help-text.md stays true. @object-ui/app-shell changes a test only.

Acceptance notes

  • Two notes from the first round (PR objectui#12236) stand as they were, not touched here, with no carrier: Studio's ObjectFormCanvas draws description only, and a plugin-detail comment lists help among the keys the spec's translateObject reads.
  • Inside the claimed files, two neighbour docblocks moved with the edit: RuntimeField.inlineHelpText's doc names fieldHelpText, and RuntimeField.description gained a one-line doc.

Generated by Claude Code

…xt moves to @object-ui/fields as the one read (objectui#12213)

The record form and a field-backed action param read a field's help line
through one function, fieldHelpText, now exported by @object-ui/fields:
inlineHelpText, else description, an empty string reading as absent. The
served help key is no longer read, per the ruling on the card: its one
producer was an objectstack server on spec 17.6.0 or earlier, and 17.7.0
serves the translated text on description.

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.2 KB 3204.6 KB
Main entry chunk (gzip) 73.5 KB 350 KB
Entry file index-DJctojK9.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) 280.08KB 71.40KB
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.78KB 38.84KB
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.09KB 70.91KB
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: 0621328684dac02a4b93051bcb2088276350e4f7
Local-runs: none

Inputs: card objectui#12213 (body and all 10 comments, ruling 6134063362 letter A and claim 6134381355 included), PR objectui#12283 (body, 13-file list, net diff against main), and the check-runs on the head. Read-only: git reads of the already-fetched refs in the objectui clone (origin/main, the dev's branch, the @object-ui/*@17.7.0 tags); nothing built, run or re-run.

① Derived judgments

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

  1. @object-ui/fields public surface widens by ONE value export, fieldHelpText (new home file fieldHelpText.ts, re-exported from the package entry). Typed as a function from a Pick of BaseFieldMetadata's inlineHelpText and description members to string | undefined. RIGHT. The ruling asked for the one read to be lifted into @object-ui/fields so the order has one home; @object-ui/fields publishes . and ./style.css only (its exports map, read on the head), so no non-public home exists; @object-ui/plugin-form already depends on @object-ui/fields and @object-ui/fields on @object-ui/types (both package.json files, read on the head), so the lift adds no dependency edge. The new module imports only a type from @object-ui/types, so it closes no cycle with the fields entry.
  2. @object-ui/plugin-form public surface: unchanged. RIGHT. plugin-form/src/index.tsx on main re-exports only sectionEntryName from ./sectionFields, so the deleted fieldHelpText function and FieldHelpTextSource interface were never reachable from the entry; the dev's 59-to-59 built-declaration count agrees.
  3. Accept set of the record form's help line narrows (both builders, ObjectForm's own and fromObjectSchema): FROM inlineHelpText || help || description TO inlineHelpText || description. RIGHT under ruling 6134063362 letter A: the served help leg retires now, no staged step (none named). The || rule is kept, so inlineHelpText: '' still falls through to description (Studio stores a cleared box as no key), which the pins hold on the flat, sectioned and tabbed layouts.
  4. Accept set of resolveActionParam's field-backed helpText fallback narrows the same way, through the same function: FROM param.helpText ?? (inlineHelpText || help || description) TO param.helpText ?? fieldHelpText(field). RIGHT. The param's own helpText still wins (??, unchanged), and the hand-restated order that the first round had to leave in the resolver is gone, which is the "one read, not two copies" direction triage gave.
  5. The two local help members go: FieldHelpTextSource.help (plugin-form) and RuntimeField.help (fields). RIGHT. On the head, every remaining non-test .help read in packages/*/src is a non-field shape: FlowNodeConfigField's FlowConfigField.help, StudioDesignSurface's startTrigger.trigger.help, flow-node-config's z.help, and i18n keys ending in .help. No reader of a field definition's help remains.
  6. Type level: the read's source type refuses help. A typed literal carrying help does not compile; pinned by a @ts-expect-error in fieldHelpText-12213.test.tsx, which packages/fields/tsconfig.test.json compiles (include is src/**/*.test.ts and src/**/*.test.tsx, chained from the package's type-check script), so the directive is live, not a phantom. RIGHT, and consistent with the authoring face: BaseFieldMetadata lost help at the first round's merge (923504f) and the head's field-types.ts still declares no help.
  7. Signature narrowing: fieldHelpText no longer accepts null | undefined. RIGHT. All three call sites guard before the call: ObjectForm's builder is inside if (field), fromObjectSchema returns a stub when !field, and resolveActionParam returns its inline-only shape when !field before the helpText line.
  8. Behaviour change for a served document that carries help: with description too, the description is drawn; with help alone, no help line. This is the ruled cost (older server, untranslated help), and the pins now assert the new meaning with weight (text absent, helpOnly draws an empty accessible description), not merely the old text's absence. RIGHT, as the ruling's "flip from loses to ignored" asked.
  9. @object-ui/types: no surface change. One docblock phrase in BaseFieldMetadata.inlineHelpText now names @object-ui/fields as fieldHelpText's home, which is now true. RIGHT. @object-ui/app-shell: a test only.
  10. The ruling's fork clause (another producer of a served field help on objectui or objectstack main means stop). The dev's one near miss, the objectstack /i18n/labels response (GetFieldLabelsResponse.labels.FIELD.help), is judged OUTSIDE the clause: it is a separate labels document, not a field definition, and objectui never requests it. Re-read on objectui main: i18n/labels and getFieldLabels hit 0 files, control i18n/translations hits packages/i18n/src/provider.tsx and spec-translations.ts. No stop was owed; none was taken silently.
  11. Docs and READMEs. packages/fields/README.md gains the export's section with the two worked values (inlineHelpText wins; empty string falls through), both consistent with the code. plugin-form/README.md's description row is a FormField key and stays true. The four content/docs/fields pages already teach inlineHelpText (one hit each on the head, zero help:), and no docs page described the served leg, so nothing is left stale.

② Semver level

  • @object-ui/fields: two minor changesets. 12213-fields-field-help-text-export.md (new) declares the export and carries **Clause-②: yes**; yes takes at least minor, met. 12213-fields-action-param-help.md (edited from patch) now states FROM param.helpText ?? field.help ?? field.description TO param.helpText ?? fieldHelpText(field) with a BREAKING section carrying the migration (run a server on spec 17.7.0 or later, same major) and the ruling's server floor. RIGHT.
  • @object-ui/plugin-form: minor (edited from patch), FROM field.help || field.description TO field.inlineHelpText || field.description, BREAKING section with the same floor and fix. The FROM side is verified against the @object-ui/plugin-form@17.7.0 tag: both builders there read field.help || field.description. RIGHT.
  • Breaking priced as minor is this repo's fixed-group policy (objectui AGENTS.md's version-alignment section: objectui's own breaking changes ship as minor with the semantics stated in the body; major is refused by check-changeset-no-major). Changeset Bump Policy and Changeset Fixed Group Check are green on the head.
  • Editing the two pending changesets rather than adding beside them: RIGHT. The three CHANGELOGs on main carry zero 12213 mentions, so both are unreleased; each said "help is still read", which this diff makes false within the same release. check-changeset-overwrite is report-only by its own header, and this is its measured legitimate case (bump-level and factual correction to an unreleased declaration). Changeset Overwrite Report is green.
  • @object-ui/types: no new changeset. The pending 12213-types-inline-help-text.md is re-read and stays true (it never names fieldHelpText's package). A docblock phrase is not a surface. @object-ui/app-shell is test-only and owes none. Changeset Declaration (presence) is green on the head.
  • Clause-②: line. PR body, second line, Clause-②: yes, no arm. RIGHT: the diff both widens a public surface (the export) and narrows an accept set (the served leg), so yes with no single arm from the closed pair is the honest declaration, and it matches claim 6134381355's reading. Not skip-changeset: four released packages' src move and two of them publish behaviour.

③ Boundary flags

Dev report 6135156455: open_questions empty, out_of_scope_findings empty. Its seven deviations, each answered:

  1. Changesets edited, not only supplemented — answered in ② (right).
  2. @object-ui/types no changeset, @object-ui/app-shell test-only — answered in ② (right).
  3. fieldHelpText drops null | undefined tolerance — answered in ① item 7 (every caller guards).
  4. Build wider than the named closure — procedural, no contract bearing; the head's check-runs are the reading of record.
  5. app-shell type-check memory at 6144 — procedural; Type Check on the head is the gate reading (see below).
  6. One 22m36s hold of the shared lock — disclosed, no contract bearing; no action in this record.
  7. Neighbour docblocks moved inside the claimed resolveActionParams.ts; ObjectForm.tsx import-only — inside the claimed files, accepted.

Further flags, answered:

  • The dev's "the PM may judge otherwise" on the /i18n/labels near miss — judged in ① item 10: outside the fork clause.
  • check:eager-closure base not measured — the head's Bundle Analysis is green and its PR comment reads 3131.2 KB of a 3204.6 KB budget; a 54-line module needs no delta.
  • PR body acceptance notes carried from round 1 (Studio's ObjectFormCanvas draws description only; a plugin-detail comment lists help among translateObject's keys): untouched by this diff, already escalated and declined filing by the first round's ACCEPT 6124052930 (code reading, no measured wrong result at a public door). They stand as notes with carrier none; not a FAIL reason here.
  • Claim surface 6134381355: all 13 files sit inside it (the three .changeset/12213-*.md, fields' home file, entry export, resolver and test, plugin-form's sectionFields.ts, ObjectForm.tsx import and test, the types docblock, the app-shell pin, the fields README). No breach, so no stop was owed.
  • Shape: draft, base main, head repo is the base repo (not a fork), first line Fixes #12213 as the ruling asked, Clause-②: yes at line start, PR assignee os-justin mirrors the card's. 13 files, +221 −101, far under the 3,000-line class. Governed surfaces: none (Governed Surface Queue Guard green). No cross-file path:line citation added (Line Citation Gate green; a scan of the added lines agrees). No model identifier in the diff.

Check-runs on the head (their conclusions are the gate verdicts), read complete shortly before this record was posted (2026-10-13T00:29Z): 33 runs, all completed, none in_progress, none red. success (30): Lint, Type Check, Repo Gates, Build & E2E, Build Docs, Test, Test (shard 1/12) through Test (shard 12/12) (all twelve), Test (dist pins), Bundle Analysis, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report, Changeset Claim Re-read, Governed Surface Queue Guard, Line Citation Gate, Spec Main Shape Gate, Live E2E (informational), label. skipped (3): Test (coverage), Test (coverage shard), dependabot, each by its workflow's own path switch, not a failure.

Implemented-by: claude/issue-12213-help-leg-retire
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 00:31
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 13, 2026 00:31
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 5b45c0d Oct 13, 2026
34 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12213-help-leg-retire branch October 13, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants