Repository navigation
fix(plugin-form): warn on an unresolvable top-level fields member (route 1) - #8859
Merged
Merged
Conversation
…` member Route 1 of objectui#8738 (ruled after route 2 landed as PR #8846): top-level `fields` on `object-form` (and its shared read path — `form` / `view:form`, and `object-master-detail-form`'s parent `fields`) reads only bare field-name strings (`{ name }` tolerated). The spec `FormFieldSchema` object that IS legal in `sections[].fields` (identity key `field`, e.g. `{ field: 'note', colSpan: 2 }`) resolves to no name here and used to vanish with no throw, no warning, no empty-state. `warnUnresolvedTopLevelField` (new export in `sectionFields.ts`, beside its existing `warnOnMixedVocabulary` sibling — same voice, same once-per-occurrence dedupe discipline) now names the skipped shape and the vocabulary difference. Wired into both read sites that resolve `fieldsToShow` this way: `SimpleObjectForm`'s loop in `ObjectForm.tsx`, and `buildFlatFields` in `flatFields.ts` (the drawer/modal path). The render outcome is unchanged -- the member is still dropped, not resolved; this is diagnostic-only, not a lenient `?? f.field` fallback (AGENTS.md #0.1 forbids exactly that). Measured, not assumed (objectui#8847): `form`/`view:form` is the identical `ObjectFormRenderer` component, and `object-master-detail-form`'s parent `fields` (no `sections`) falls through to the same `SimpleObjectForm` branch regardless of its own `formType` -- both inherit the warning for free, pinned by a new test rendering `MasterDetailForm` directly. Both registrations' `description` in `index.tsx` now also state the vocabulary, closing #8847's remaining documentation half. Test changes to the existing `objectFormFieldsMembers-8071.test.tsx` probe: row 4's `expect(warn).not.toHaveBeenCalled()` documented the PRE-route-1 silence and is now the opposite of the ruled behavior for that exact input, so it is updated to assert the warning fires with the expected content (the render assertion -- still an empty result -- is unchanged, and rows 1/2/3/5/6 are byte-for-bit unchanged). A new pin below row 6 (and a matching one in `flatFields.test.ts`) asserts route 1's required firing control: fires for an unresolvable member, does not fire for a legal bare name. `scripts/check-doc-example-types.mjs`'s `UNGATED_EXAMPLES` ledger cited `ObjectForm.tsx:123 ObjectForm` (objectui#8614 hazard) -- the new import line shifted its `@example` block to :124; the ledger key is updated to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
`registry-inputs-spec-parity.test.ts`'s `MEMBER_PINS['object-form.fields']`
quoted the pre-route-1 behavior verbatim ("dropped in SILENCE (no throw, no
warning)"), which objectui#8738 route 1 makes false for that exact input.
The mechanical check on this map (`memberPinProblem`) only verifies the pin
file exists, is collected by the runner, and names the block + key -- it
does not diff this prose against the file's content -- so this was
documentation staleness, not a behavior-check break. Corrected for accuracy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Contributor
✅ Console Performance Budget
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
Size Limits
|
1 similar comment
Contributor
✅ Console Performance Budget
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
Size Limits
|
This was referenced Sep 9, 2026
os-warren
marked this pull request as ready for review
September 9, 2026 15:59
os-warren
pushed a commit
that referenced
this pull request
Sep 9, 2026
…(objectui#8847) `embeddable-form`'s top-level `fields` registration input had no description, leaving the same trap #8738/#8846 already closed for `object-form`: an author writing the spec `FormFieldSchema` object (`{ field: 'note' }`, legal in `sections[].fields`) into this key resolves to no name and is silently skipped. Add the description, modelled on `object-form` and `form`'s. `EmbeddableForm` passes `config.fields` straight into `<ObjectForm>` with no `sections`, so it renders through the same `SimpleObjectForm` read site — measured with a throwaway render probe (not committed) that it already inherits route 1's `console.warn` (objectui#8738/#8859): fires once for `fields: [{ field: 'note' }]`, does not fire for `fields: ['note']`. No behaviour change. Description text only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Sep 17, 2026
…(objectui#8847) (objectstack-ai#8869) `embeddable-form`'s top-level `fields` registration input had no description, leaving the same trap objectstack-ai#8738/objectstack-ai#8846 already closed for `object-form`: an author writing the spec `FormFieldSchema` object (`{ field: 'note' }`, legal in `sections[].fields`) into this key resolves to no name and is silently skipped. Add the description, modelled on `object-form` and `form`'s. `EmbeddableForm` passes `config.fields` straight into `<ObjectForm>` with no `sections`, so it renders through the same `SimpleObjectForm` read site — measured with a throwaway render probe (not committed) that it already inherits route 1's `console.warn` (objectui#8738/objectstack-ai#8859): fires once for `fields: [{ field: 'note' }]`, does not fire for `fields: ['note']`. No behaviour change. Description text only. Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #8738
Refs #8847
What
Route 1 of #8738 (ruled at issuecomment-5603577602, after route 2 landed as #8846): a top-level
fieldsmember that resolves to no name now fires a namedconsole.warninstead of vanishing in total silence.sectionFields.ts: new exportedwarnUnresolvedTopLevelField(entry, objectName), right beside its existing siblingwarnOnMixedVocabulary— same voice, same once-per-occurrence dedupe discipline (a module-levelSetkeyed by object + skipped shape). It only warns; it never resolves a name fromentry— no?? f.fieldfallback, per the card's own standing prohibition.ObjectForm.tsx'sSimpleObjectFormfieldsToShowloop.flatFields.ts'sbuildFlatFields(the drawer/modal path, identical shape).{ field: 'note' }) and the vocabulary difference (sections[].fieldsaccepts the specFormFieldSchemaobject, identity keyfield; top-levelfieldstakes bare names only).#8847 — measured, not assumed
Re-derived on this branch's base (
42df92809, post-#8846):(Note: the claim comment's
:346forobject-master-detail-formwas stale on this tree — line 346 isembeddable-form'sfields, not master-detail's. Re-derived directly againstindex.tsxonorigin/main; see the report'sout_of_scope_findingsfor that third surface.)Coverage measured, both surfaces inherit route 1 for free:
form/view:formis registered on the exact sameObjectFormRenderercomponent asobject-form— same code path by construction, not merely similar.object-master-detail-form's parentfields(nosections) is passed straight throughMasterDetailForm'sparentSchemainto<ObjectForm>; every sectioned branch inObjectForm.tsx's formType routing guards onschema.sections?.length, so a fields-only parent always falls through toSimpleObjectFormregardless offormType. New pin:__tests__/topLevelFieldsWarnCoverage-8847.test.tsx, renderingMasterDetailFormdirectly and asserting the warning fires (and does not fire for a legal bare name — the firing control).⇒ #8847's remaining work was documentation-only for both surfaces, closed here by extending/adding their registration
description. Left asRefs(notFixes) since the closing decision belongs to whoever reviews the measurement, but from this seat both halves are closed.Existing probe (objectFormFieldsMembers-8071.test.tsx) — ran first, one assertion moved, on purpose
Ran unmodified against the new source change before touching the test file:
The only failure was row 4's
expect(warn).not.toHaveBeenCalled()— which encoded the PRE-route-1 silence for the exact{ field: 'note' }input the ruling requires to warn on. Rows 1/2/3/5/6 (all render-output assertions) were untouched. This is not "changed behaviour beyond a warning" — the render outcome in row 4 (toEqual([])) is byte-for-bit identical; only the "no warning happened" assumption is falsified, along exactly the axis the ruling changed on purpose.Resolution: updated row 4 to assert the warning fires with the expected content (kept the render assertion), updated its title/comment and the file's top docblock (which also said "no console warning"), and added a new pin below row 6 with route 1's required firing control (fires for an unresolvable member, does not fire for a legal bare name) — mirrored in
flatFields.test.tsfor the drawer/modal path.Re-ran after the test-file edit:
Tests
pnpm exec vitest run packages/plugin-form/src/__tests__/objectFormFieldsMembers-8071.test.tsx— EXIT=0, 8 passed (8) (was 6; +2 new pin), correct worktree path in output.pnpm exec vitest run packages/plugin-form/src/__tests__/topLevelFieldsWarnCoverage-8847.test.tsx— EXIT=0, 2 passed (2).pnpm --filter @object-ui/plugin-form test— EXIT=0, 89 files / 883 passed, 1 skipped (884) (was 88/877+1skip before this change).pnpm --filter '@object-ui/plugin-form^...' buildthenpnpm --filter @object-ui/plugin-form run type-check— both EXIT=0 clean.pnpm --filter '@object-ui/console^...' build(dependency closure, 35/47 projects) thenpnpm --filter @object-ui/plugin-form build— EXIT=0.pnpm --filter @object-ui/console run type-check— EXIT=0 clean.pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts— EXIT=0, 198 passed (198). Itsobject-form.fieldsMEMBER_PINSentry quoted the pre-route-1 "dropped in SILENCE (no throw, no warning)" text — updated for accuracy (the entry's mechanical check only verifies the pin file exists/is collected/names the block+key, so this was documentation staleness, not a behavior-check break).pnpm --filter @object-ui/console test(full sweep, required by dispatch) — run to completion; see the report comment on finding(plugin-form):object-formcarries TWO surfaces spelledfieldswith different member vocabularies — the specFormFieldSchemaobject is silently dropped by the top-level one #8738 for the captured count.Ledger hazard (objectui#8614) —
check-doc-example-types.mjsUNGATED_EXAMPLEScitedpackages/plugin-form/src/ObjectForm.tsx:123 ObjectForm. This diff's one new import line (forwarnUnresolvedTopLevelField) shifted the@exampleJSDoc block from :123 to :124 — confirmed by grepping@examplebefore/after. Updated the ledger key to:124in the same commit.pnpm check:doc-examplesitself requires a fullturbo run buildacross ~24 packages (farm-scale, not a targeted gate) so it was not run locally; the line-shift fix is evidence-based (direct before/after grep), not a guess.Scope note (Clause-②: no)
No accept set, key, shape, or arm moves. No schema touched.
Changeset
.changeset/8738-fields-warn-route1.md—@object-ui/plugin-formpatch (new runtime output on a published package).Generated by Claude Code