Repository navigation
feat(app-shell): the create entry uses the object's create form view when declared - #13117
Conversation
…when declared A create selects the object's `create` form view (served as `OBJECT.create`) when it declares one, else the default `form` / legacy `formViews.default`; an edit is unchanged. The flow is passed to the resolver explicitly by the console's New/Edit modal and the action modal, from the same mode the form opens in. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
… pin Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
|
changeset-claim-re-read
|
✅ 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
|
… too RecordFormPage selects its form view through selectRecordFormView with its own mode, so a page-mode create renders the object's create form view when declared and a page-mode edit keeps the default form view. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
✅ 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
|
|
Re-queued by Generated by Claude Code |
Fixes #13099
Clause-②: no
What
The create entry uses the object's
createform view when the object declares one, else today'sform/formViews.default. An edit is unchanged. This is ruling B′ on objectstack-ai/objectstack#24692 (ruling comment 6281834942), scoped by the director's pointer 6281857581 on the card. Its first bullet, quoted verbatim as the director asked:recordFormNavigation.ts: newselectRecordFormView(objectDef, flow)holds the one selection.resolveFormViewLayoutandresolveRecordFormModalLayouttake an optionalflow('create' | 'edit'). A caller that names no flow gets the default form view, the same answer as before.AppContent(the console's New/Edit modal) passeseditingRecord ? 'edit' : 'create'. That is the same test that sets the modal's ownmode, which comes from the?form=URL param (newor a record id). So the selected view always matches the mode the form opens in, and an edit cannot reach the create selection.useActionModal(action-opened object forms) passes the flow from the samemodetheModalFormopens in.viewandeditkeep the default form view.The served key:
OBJECT.create, not a barecreate(mechanism, measured)The pointer's literal spelling
objectDef.formViews?.createdoes not match the runtime shape. A container'sformViews.createexpands to the form item namedOBJECT.create(expandViewContainerin@objectstack/spec/ui).MetadataProvider.mergeViewsIntoObjectskeysobjectDef.formViewsby that served name. The existing pin "showcase_contactkeeps its default whichever order its two forms arrive in" (MetadataProvider.merge.test.ts) asserts the keysshowcase_contact.create/showcase_contact.form.A literal
formViews.createread would therefore never match the documented showcase example, and a pin on a hand-built fixture would pass anyway. The selection readsformViews[OBJECT.create], and the new pin builds its fixtures from served rows run through the real merge. No second spelling is read; there is no??alias between the two.Not moved (per the ruling)
There is no
mode/formViewplumbing and no inline add. The Add button keeps its position and itsenabled: truegate. Thep1-spec-alignmentparity row is untouched.Pins (
packages/app-shell/src/utils/__tests__/createFormView-13099.test.ts)showcase_contact(acreateform view whose sections and size differ from the defaultform): a create selects thecreateview's sections andmodalSize, throughresolveFormViewLayoutandresolveRecordFormModalLayout.form, which is tabbed with different sections. A call with no flow gives the same answer as an edit.crm_leadhas a named Web-to-Lead form and nocreateform. Its create usesform, and the named form is never picked. The legacyformViews.defaultis still the create fallback. A named-only object creates with no layout.Ablation, run through
ablation-replace.mjsat HEAD2e759c1083. The mutation replacesif (createView) return createView;with a no-op, and the anchor goes from 1 hit to 0. Result: (a) red withexpected [ 'identity', 'notes' ] to deeply equal [ 'quick' ], (b) and (c) green (1 failed | 2 passed). After the restore, the blob isdf4f0b151ec4b5ebaef09a6aef65424f38e6c873, equal to HEAD, andgit diff HEADis empty.Verification (HEAD
2e759c1083)pnpm --filter '@object-ui/app-shell^...' build(the closure build): exit 0. Thenpnpm --filter @object-ui/app-shell type-check: exit 0. That istsc --noEmitplustsc -p tsconfig.test.json, and--listFilescounts the new test file in the test project.--maxWorkers=1, under the lock): the new pin plus every test file namingrecordFormNavigation/useActionModal/AppContent, 99 files in three batches. Results: 34 files / 260 tests, 31 / 235 and 35 / 338, all passed.packages/app-shell/directory. The dispatch ruled out whole-package-directory runs locally..ts/.tsxfiles witheslint --no-inline-config --format json: 4 files, 0 errors. The warnings are 43 / 15 / 5 on the three edited files, equal to their base-commit counts, and 0 on the new test. The rooteslint.config.jsnames noparserOptions/projectService/ type-checked preset, so linting is not type-aware. This diff cannot move any untouched file's verdict.check:phantom-deps,self-import,unreferenced-sources,doc-example-readers,handler-key-reads,side-effects-array,element-data-source-declaration,spec-symbols,action-forward-parity,designer-field-key-parity,icon-record-names,i18n-keys,i18n-drift,i18n-designer-parity,control-bytesandnew-line-citationsall exited 0. So didcheck-hand-rolled-comment-mask.mjs,check-changeset-fixed.mjsandcheck-changeset-presence.mjs.check:spec-floors: exit 1,no-artifact. The workspace is not fully built, so this is NOT MEASURED, recorded and not fixed, as the dispatch says.Acceptance notes
createform view is selected, it is the whole form: its own sections, layout, size and subforms. Model-derived inline subforms (attachInlineSubforms) attach to the default form, so they do not appear on such a create.createin the same container takes the nameOBJECT.createfirst. The form is then renamedOBJECT.create_2by the expansion, which stamps its own collision warning, and the create falls back to the default form.Generated by Claude Code