Repository navigation
fix(plugin-form): a create form preselects the option a field marks default: true (objectui#11914) - #11944
Merged
objectstack-fleet[bot] merged 5 commits intoOct 8, 2026
Conversation
…efault: true A field that declares no `defaultValue` but marks one option `default: true` opened on the "Select an option" placeholder, while a create that omitted the field stored the marked option: the server's insert path falls back to it since objectstack#7246. `schemaDefaultValues`, the one function every create-form container seeds through, now reads the option list the same way the engine's `resolveOptionDefault` does: only when `defaultValue` is absent (== null), first marked option on a single-valued field, every marked option as an array on a multi-valued one (`isMultiValueField`). Edit forms and caller seeds are unchanged. Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
…t: true The README said an option's `default: true` leaves a create form empty because the server reads `defaultValue` alone. The server falls back to the marked option, and the form now does too, so the table row and the paragraph under it state the rule the form applies. Adds the patch changeset for the fix. Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
…rce double Drops the `as any` casts from the new pin: the container schemas are typed by `ModalFormSchema` and `ObjectFormSchema`, and the data-source double is a named partial. Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
…ault A `multiple` flag on a type outside the multi-valued sets keeps the seeded default a scalar, and an option whose value is spelled `current_user` seeds that literal, never the acting user's id: both are what the engine stores for an omitted field. Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
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. Which half objected:
📦 Bundle Size Report
Size Limits
|
This was referenced Oct 8, 2026
…tion Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
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
|
objectstack-fleet
Bot
deleted the
claude/issue-11914-select-default-option
branch
October 8, 2026 12:37
This was referenced Oct 8, 2026
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Oct 9, 2026
…1) and the Interfaces page-create (objectui#11932) (objectstack-ai#11967) Part of objectstack-ai#11861 Re-lands objectui#11823 step 3: objectui#11932, the Studio Interfaces page-create (seat `domain:ui#3`). That card stays open. Clause-②: no Held as a draft until objectui#11949 (the +5,120 B allowance, ruling `6056819248` step 2) lands. When this PR was opened, objectui#11949 was already on `main` as `1c51e973b`, and this branch has merged it. ## What this is A plain `git revert 3c888c6`, followed by merges of `main`. `3c888c6` is the rollback PR objectui#11956, a single-parent squash, so `-m 1` does not apply. It brings back: - objectui#11931: Studio's validation "New" menu opens on common rules, with the rule types under "Advanced". This is the validation-rule entry of objectui#11861. - objectui#11932: objectui#11823 step 3. The Interfaces pillar creates a page and opens it on its source beside a live preview. Claim `6057967120` on objectui#11861 · review `6057399892` on objectui#11937 · ruling `6056819248` on objectui#11942. ## Tree proof - The revert commit `7eeffd29b` has the tree of `f3a0488` (`011fe521e`), so `git diff f3a0488 7eeffd2 --stat` prints nothing. - `.changeset/11937-eager-budget-rollback.md` was added by `3c888c6` and never existed at `f3a0488`. Its removal therefore shows against `3c888c6`, not against `f3a0488`. - Against `main`, the branch changes the 15 paths `3c888c6` changed, and no other path. - Twelve of the 15 hold their `f3a0488` blob byte for byte. The other three were also changed on `main` after `3c888c6`, and they carry both intents (next section). ## Merges of main, and the conflicts The branch merged `main` five times: at `1c51e973b`, `d73d98770`, `70e3d7721`, `ccc2824cf` and `247f50349`. Only two of the PRs these brought in touch a restored file. **objectui#11945** (objectui#11923, the *Runs on* row), merged at `70e3d7721`. - One conflicting hunk, in the import block of `ObjectValidationsPanel.tsx`. This side imports `validationPresets.js`; `main` imports `ScriptValidationSchema` / `ScriptValidationParsed` from `@objectstack/spec/data`. Both lines are kept. - Two files merged without conflict: - `metadata-admin/i18n.ts`: `main` removes the `engine.studio.rules.event.delete` row, en and zh. It stays removed. - the `newRuleWaits-11820` pin: `main` asserts there is no Delete box. That assertion stays. - Check, per file: the changed lines of the merge against `main` equal the re-land's own changed lines (`3c888c6..f3a0488`). The changed lines of the merge against this branch's previous head equal objectui#11945's own diff. Both comparisons were identical for all three files. **objectui#11935** (objectui#11894, typed is-empty operators), merged at `247f50349`. - Two conflicting hunks, both in `ObjectValidationsPanel.tsx`. Each side added the same `FieldOpt` member, `type`. - The interface keeps one `type`, `main`'s `multiple` and the presets' `system`. One doc comment names both readers of `type`. - The field mapping keeps one `type` line, `main`'s `multiple` line with its comment, and the presets' `system` line. - `i18n.ts` merged without conflict, and both sides' rows are kept. - The same check, run on this merge, leaves only the shared `type` member and the merged comments as residual lines. `main` moved on after `247f50349` (objectui#11961, objectstack-ai#11962, objectstack-ai#11944). None of those touches any of the 15 paths, so they are not merged here. ## Changesets - Restored: `.changeset/11861-validation-presets.md` and `.changeset/11823-page-create.md`, both `@object-ui/app-shell: patch`. - Removed: `.changeset/11937-eager-budget-rollback.md` (`@object-ui/app-shell: patch`). This re-land supersedes it. - `check-changeset-overwrite` is report-only and exits 0. It reports one pre-existing changeset deleted, with `@object-ui/app-shell` "GONE from the declaration" of that file. - The package still bumps, because both restored changesets declare it. This is the gate's case 3: a changeset superseded by a replacement in the same change. ## Gates, on head `fb0c91d45` (every merge above included) Tests use the repo-root `pnpm exec vitest run` and run under the shared verify lock. - **Named set:** 98 files and 631 tests, all passed. It covers: - all eight `ObjectValidationsPanel*` suites, including objectui#11945's `runsOn-11923` and objectui#11935's `typedEmptyOps-11894`; - every test file that imports `interfaceCreate` or `StudioDesignSurface`; - `column-identity.ratchet.test.ts`; - the `main` pins next to the conflicts: `ConditionBuilder.typedEmptyOps-11894`, `PagePreview.pageKind-11933` and `SourcePageEditor.pageKind-11933`. - **Widened set:** 87 files and 1,103 tests, all passed. It covers every other test that imports `validationPresets`, `ObjectValidationsPanel` or the designer string table `metadata-admin/i18n.ts`. - `pnpm --filter @object-ui/app-shell type-check` passes, after a turbo build of the app-shell dependency closure (28 of 28 tasks). - Each of these gates exits 0: - `check:i18n-keys`, `check:i18n-designer-parity`, `check:i18n-drift`; - `check-changeset-presence`, `check-changeset-fixed`, `check-changeset-no-major`, `check:changeset-claims`, `check:pending-changeset-literals`, `check-changeset-overwrite` (report-only, see above); - `check:unreferenced-sources`. - eslint on the 11 touched source and test files reports 0 errors and 22 warnings. The repo sets no `--max-warnings`. The warnings are the ones already in these files at `f3a0488`, and the merge adds none. - `check-governed-queue-guard --test` reports NOT GOVERNED: none of the 15 paths is on a governed surface. - Not run: the console build. CI's `Bundle Analysis` measures the first-load bytes against the ceiling that objectui#11949 raised. `scripts/check-eager-closure-budget.mjs` is not touched. - Reference point: on its push run, `f3a0488` (the tree this re-land restores) had 66 check runs succeed, 6 skipped and 1 fail, `Bundle Analysis`. That is 73 of 73 runs read. --- _Generated by [Claude Code](https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU)_ 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 #11914
Clause-②: no
What changes
A create form preselects the option a field's option list marks
default: true, when the field declares nodefaultValue. This is the rule the server's insert path applies when a create omits the field. The objectstack tutorial's Create Ticket form now opens with Priority = Low and Status = Open, and submits without a manual pick.Where. The fix is in
packages/plugin-form/src/schemaDefaults.ts, inschemaDefaultValues. That is the one function every object-form container seeds a create form through (ObjectFormdirectly;ModalForm,DrawerForm,TabbedForm,SplitFormandWizardFormthroughseedCreateValues). A new module-privateoptionListDefaultreads the option list. No second derivation was added in the select widget.Measured first, on
mainf1781be3a(H0)The new pin was run against the unchanged source. It rendered the console's create dialog (the global
ModalForm) on the tutorial's ticket object, verbatim. 11 of 17 tests failed. The main failure:AssertionError: expected 'Select an option' to contain 'Low'. Onmain,schemaDefaultValuesreturns{}for that object, so the premise holds on currentmain. The module docblock said it deliberately ignored the option flag "because the server readsdefaultValuealone". That stopped being true when the engine began falling back to the marked option (objectstack#7246).Bound: H0 rendered the real
ModalForm,ObjectFormand field widgets with React Testing Library. It did not drive a booted console in a browser against a live backend.The rule: the engine's, read client-side (H1, H2, H3)
This is a read-only check of objectstack
ObjectQL.applyFieldDefaults/resolveOptionDefaultand its testengine-select-option-default.test.ts. The client applies the same rule:default: true, nodefaultValuedefaultValue+ option flagdefaultValuedefaultValue(unchanged)defaultValue(NOW(),current_user, CEL) + option flagdefaultValue: null+ option flag== nullpresence test)isMultiValueField:multiselect, orselect+multiple)multiple: trueon a type outside the multi setscurrent_userThe client asks
@objectstack/spec'sisMultiValueFieldfor the arity, which is the engine's own predicate. It never readsmultipleraw. Only the canonicaldefaultkey is read: the spec's alias layer normalizesisDefault/selectedbefore metadata is served.What does not change:
initialData/initialValues) and the user's own pick still win.omitServerResolvedDefaultsandisRequiredInFormare untouched. An option value is a static literal, so it is submitted like any other seeded value, and a required field the user clears is still refused.Public surface: unchanged
schemaDefaultValuesandseedCreateValuesare not on the package entry. They now take a package-localSeedSchemaLike, derived from the publishedFieldDefaultsSchemaLikeplusoptions/multiple. The publishedFieldDefaultsSchemaLikekeeps exactly its three pinned members (parameterTypesNameable-7324.test.tspasses). No export, prop, type member orpackages/i18nkey changes. That is why the PR declaresClause-②: no.Pins and ablation
packages/plugin-form/src/__tests__/optionDefaultSeed-11914.test.tsx, 19 tests:schemaDefaultValuesunit cases for every row of the table above, plus the control: options that mark nothing seed nothing.ModalForm(console create dialog), on the tutorial ticket object:multiselectpreselects every marked option (aria-pressed);ObjectForm(page-mode create): preselected on create, not applied on edit.Ablation, run after the fix was committed:
scripts/ablation-replace.mjs(objectstack) replacedconst fromOptions = optionListDefault(f);with anundefinedassignment.a8807e5etobd538283.main.git diff HEADis empty.dist/is on the measured path.Eager closure (Bundle Analysis)
plugin-formis eager, so this was measured under the verify lock. The consolevite buildran twice from the same worktree, pluscheck-eager-closure-budget.mjs. The head build was at27be779f7. The base build was the same tree withschemaDefaults.tsreverted tof1781be3a; the restore was proven by blob.f1781be3a)The growth is in the
plugin-formchunk (+154 gzip). Other chunks moved by 1 to 3 bytes from hashed import names. Thevendor-objectstackchunk did not move, soisMultiValueFieldwas already in the closure. The change is kept minimal.Note:
main19a7348is already a few bytes over the 3307.0 KB ceiling (the seat's main-red card, objectui#11937). This PR'sBundle Analysisis expected red until that card's change lands. It is not addressed here, andscripts/check-eager-closure-budget.mjsis untouched.Docs
packages/plugin-form/README.md, "What a create form opens with":default: true: empty" is replaced.defaultValueis read.Both sentences became false with this change. This file is outside the claim's file surface. It was edited because the code change made a published sentence false, and the objectui
AGENTS.mddocs-driven rule (#2) applies. The report names it as a deviation; it can be dropped from the branch if the seat rules otherwise. No sentence undercontent/docs/**orskills/**covers select defaults on create forms. A grep for the option flag and for create-form defaults found onlydefaultValuedocs about other components.Acceptance notes (not changed here)
apps/console/src/components/FormPage.tsxserves/forms/:nameand/f/:slug. It seeds through its ownreadPrefill, which still readsdefaultValuealone, so an option-default select there would still open empty. This is a reading of the code, not a measured failure. It is outside this claim's surface (apps/console). Sharing the rule would need a published helper, which would be aClause-②question. Carrier: none named; left to the seat.InlineQuickAddand the calendar's quick-create payload readdefaultValuethe same way. They were not measured, and no carrier is named.Gates, at head
6da0e6e0bpnpm exec vitest runon:schemaDefaults(createDefaults,currentUserDefault,entrySurface-6059,initialRecordMerge-9760,parameterTypesNameable-7324);column-identity.ratchet.test.ts.Result:
Test Files 7 passed (7),Tests 141 passed (141).pnpm --filter @object-ui/plugin-form type-check(tsc --noEmit && tsc -p tsconfig.test.json) exits 0, after building the@object-ui/plugin-form^...closure.--listFilescounts the new test once.pnpm exec eslinton the two touched source/test files: 2 files, 0 errors, 0 warnings. ESLint config has no type-aware linting, so untouched files cannot change verdict.All exit 0:
node scripts/check-changeset-presence.mjsnode scripts/check-changeset-no-major.mjsnode scripts/check-changeset-overwrite.mjspnpm check:changeset-claimspnpm check:pending-changeset-literalspnpm check:spec-symbolspnpm check:new-line-citations(0 new)pnpm check:control-bytesnode scripts/check-test-path-roots.mjsnode scripts/check-vi-mock-override-shape.mjsnode scripts/check-doc-links.mjsNot run locally (CI): the repo-wide
pnpm lint/pnpm check,readme-exports(no export list changed) anddoc-snippet-types(no code fence added).Changeset:
.changeset/11914-select-option-default.md,@object-ui/plugin-formpatch.Session:
https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAUGenerated by Claude Code