Skip to content

fix(console): a served form row marked required holds Save when left empty - #28293

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-xe3kz5
Oct 31, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-xe3kz5

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #28179

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:元数据新建/编辑页现在认服务端表单行上的 required: true:这类行留空时,该字段下显示「This field is required.」,点保存不发请求;适用于所有元数据类型,不是权限集特例。
  • 为什么改:fix(spec): the permission-set form asks for the label first, and requires it #28152 之后新建权限集的「Label」带必填星号,但 PermissionSetSchema.label 仍是可选,页面只按 Zod 校验、服务端也接受,所以留空照样保存成功,星号只是装饰。
  • 风险与回滚:只拦表单当前提供且可编辑的行(隐藏、只读、谓词隐藏的行不算),作者总能填上解锁;schema 自己要求的字段维持原判定,服务端接受集不变;回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx(requiredRowIssues 与 doSave 中紧随 Name 拦截的那段),SchemaForm.tsx(SectionedSchemaForm 的 visibleFields / requiredRowKey),ResourceEditPage.requiredRow-28179.test.tsx。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/ResourceEditPage.requiredRow-28179.test.tsx,预期 3 个用例全绿;或在 Console 设置 › 权限集 › 新建,只填 Name 点保存,Label 下出现必填提示且没有保存。
  • 需要你做的:无,席位落地。

What changes

  • SchemaForm gains an optional onRequiredRowFieldsChange(fields) prop. SectionedSchemaForm reports the top-level rows it offers whose served row says required: true: in a visible section (collapsed sections and other tabs count), not hidden, with a schema property, and editable (not readonly, not immutable outside create, not a read-only form). The row visibility filter is now one local function (visibleFields) that the rendered rows, the tab list and this report all read, so the hold and what is on screen cannot disagree. The callback is keyed on the joined field names, never on an array identity, and reports an empty set on unmount. Any non-sectioned face reports nothing.
  • ResourceEditPage keeps those fields beside the existing inlineIssueFields state and derives requiredRowIssues from the draft: a reported field that is empty (absent, null, blank string, empty array) is a located issue with the new engine.validation.fieldRequired message. It is skipped where the served schema already requires the field (that field keeps its own verdict) or gives it a default. The issues join displayIssues, and doSave returns before any request while one stands, right after the existing Name hold, on create and on edit.
  • i18n: engine.validation.fieldRequired in the studio en and zh packs (the only two the namespace ships), one block each.

Why

A served form row's required is a layout override (FormFieldSchema.required, "Required override"). PR #28152 made the permission set's Label row required while PermissionSetSchema.label stays z.string().optional(), so the live Zod pass, the save-time checks and the server all accepted an empty Label: the star was the only thing that said otherwise.

Why this predicted verdict may hold Save when the live Zod pass may not: the advisory ruling at blockingReport guards against a client stricter than the server dead-bolting a draft the author cannot fix. Only rows the form on screen offers and lets the author edit report, so the author can always fill what holds the save, the same bound the Name hold (#27865) relies on.

Tests

  • ResourceEditPage.requiredRow-28179.test.tsx (real page, real MetadataClient over an in-memory server, real served form from METADATA_FORM_REGISTRY.permission):
    • (a) New Permission Set, Label empty, Name sales_ops, Save: the inline issue is on the Label row and nowhere else, no PUT leaves; typing a Label clears it and Save sends { name: 'sales_ops', label: 'Sales Ops' }.
    • (b) CONTROL: the same served form with the Label row's required removed: an empty Label saves.
    • (c) a served schema that requires label: the page gives the verdict it gave before (the create seed's placeholder refusal, objectui#12810), and the new message is not added.
  • SchemaForm.requiredRows-28179.test.tsx: which rows are reported: an offered required row (also in a collapsed section); a predicate-hidden row only once its predicate shows it; readonly, immutable outside create and a read-only form are left out; a host-hidden row is left out; the flat fallback reports nothing; unmount reports an empty set.

Acceptance notes

  • Measured first (order step 1), as the page test on current main (6285203d): with the served permission form, the Label row draws the star and no issue appears after Save (reverse leg below). The page on main never reads a row's required outside the star, so Save takes the same path as CONTROL (b): the PUT goes out with no label and the page lands. The server accepting it follows from PermissionSetSchema.label being optional; not measured live against a running server.
  • Zod-required rows behave as before by design: the rule only adds a verdict where the row is stricter than the served schema. A field both require keeps one message (the schema's) and the old refusal path.
  • A required row in an inactive tab draws its issue only when its tab is mounted; until then it is named through the existing summary banner, which leaves out only fields drawn inline. Not separately pinned.
  • ui/AGENTS.md ✨ Set up Copilot instructions #2 (docs): no README or content/docs/frontend/ page describes the metadata editor's save checks, so no doc page changed.
  • The order placed the client-validation region at about L940 to L970; that range is the advisory-ruling comment. The hold sits in doSave beside the Setup → metadata edit page saves a bad name anyway and stacks three messages (server refusal, client summary, inline error) (#27521 row 16) #27865 Name hold, as the claim's file surface records. No line in Studio: one item toolbar — layers, references, history with rollback, audit, source — on every pillar item #28239's regions (import block, references code, item-tool sheets) changed.

Verification

All local readings on head 75f14a8c unless stated.

New tests, and the reverse leg on main's sources
  • Fix: pnpm --dir ui exec vitest run on the two new files: Test Files 2 passed (2), Tests 9 passed (9).
  • Reverse leg: ResourceEditPage.tsx and SchemaForm.tsx restored to base 6285203d (marker counts after the restore: fieldRequired 0, onRequiredRowFieldsChange 0), same command: Tests 6 failed | 3 passed (9); the 6 are the five SchemaForm report cases and page case (a) (Unable to find an element by: [data-testid="schema-form-issue"] inside the Label row, which shows only its star). (b) and (c) pass on both trees. Restore proven by an empty git diff HEAD.
Neighbouring suites, type-check, lint
  • Every ResourceEditPage.*.test.tsx and SchemaForm*.test.tsx in metadata-admin plus packages/i18n/src/__tests__/studio-namespace.test.ts: Test Files 78 passed (78), Tests 568 passed (568).
  • pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run type-check (after a dts build of @object-ui/app-shell^...): exit 0.
  • eslint on the changed files: 0 errors (warnings pre-existing). With --no-inline-config it reports the three react-hooks/static-components lines main already disables inline; none is in this diff.
  • pnpm --dir ui run check:new-line-citations: 0 new citation(s), exit 0.
Derived gates (dispatch-gates --commands, 34 families)

All 34 exit 0, including node scripts/check-sdui-manifest.mjs (and --self-test), pnpm --dir ui exec vitest run scripts/, check:adr-0087-registration, check:changeset-no-major, check:empty-changeset, check:published-files, check:type-check-coverage, check:type-check-debt, check:test-source-alias, check:cross-package-test-inputs, check:undeclared-dep-imports, check:nul-bytes. dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. The full farm is CI's.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (untouched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (changed files linted locally; the repo-wide run is CI's)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L9qibzECC3G6Nyga7Ysbjd


Generated by Claude Code

…t empty

A form row's required flag is a layout override the schema may not share,
so the live Zod pass and the server both accept the empty field. SchemaForm
now reports the rows it offers whose served row says required (visible,
editable), and the metadata edit page shows an unset one as the field's
required error and holds Save on it.

Claude-Session: https://claude.ai/code/session_01L9qibzECC3G6Nyga7Ysbjd
Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Oct 31, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-tesla · session_01L9qibz8HEj53Wx2uphTo2o

维护者速读

  • 改了什么:元数据新建/编辑页上,服务端表单标为必填、但类型 schema 允许为空的那一行(如权限集的「Label」),留空时现在会在该字段下显示「This field is required.」并拦住保存,所有元数据类型通用。
  • 为什么改:console(metadata-admin): a served form row marked required that the Zod leaves optional is only a star, so Save accepts it empty (permission set Label after #28152) #28179:fix(spec): the permission-set form asks for the label first, and requires it #28152 把权限集 Label 的表单行设为必填后,页面只画了星号,保存照样把空 Label 存进去。
  • 风险与回滚:只在「表单行比 schema 更严」的字段上生效;schema 本身必填的字段沿用原判定;隐藏或只读的行从不拦保存,作者总能填到挡住保存的字段。服务端接受规则不变。回滚即 revert。
  • 先看:ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx 的 requiredRowIssues 与 doSave 里的拦截,SchemaForm.tsx 新增的 onRequiredRowFieldsChange。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/ResourceEditPage.requiredRow-28179.test.tsx packages/app-shell/src/views/metadata-admin/SchemaForm.requiredRows-28179.test.tsx,全部通过。
  • 需要你做的:无,席位落地。

席位意见:「哪些行算提供给作者」只用表单渲染时同一条可见性规则判断,不另抄一份;拦截只在表单比 schema 更严处生效,不改服务端,可直接落地。

席位核对

ACCEPT · seat domain:ui#3 · head 75f14a8c · 2026-10-31T03:50Z

  1. Shape: Fixes #28179, draft, base main; 7 files, +412/−19: SchemaForm.tsx gains optional onRequiredRowFieldsChange reported by SectionedSchemaForm from one visibleFields rule (seat-compared with main's renderSection filter: identical; the tab filter now reuses it, same predicate) and rowReadOnly; ResourceEditPage.tsx keeps the set (≈L646), builds requiredRowIssues only where the schema neither requires nor defaults the field, joins them into displayIssues, and holds doSave after the Setup → metadata edit page saves a bad name anyway and stacks three messages (server refusal, client summary, inline error) (#27521 row 16) #27865 Name hold (create and edit); key engine.validation.fieldRequired (studio en/zh); two new suites; changeset @object-ui/app-shell + @object-ui/i18n patch. Seat read of the diff.
  2. Premise measured: the reverse leg on main's sources reds 6 of the new cases (dev); case (a) asserts PermissionSetSchema leaves label optional and the served form marks it required, so the hole is real; (b) CONTROL keeps an unrequired row optional; (c) a schema-required field keeps its old verdict.
  3. Gates / check-runs: on head 75f14a8c every latest check-run is success or skipped (44 / 11, 0 failure), mergeable_state clean. Dev: 34/34 derived gates exit 0, --ran 0 NOT-MEASURED; 78 metadata-admin files / 568 tests; app-shell + i18n type-check exit 0; check-sdui-manifest exit 0 (os-dev-report 6538468723); writes relay 2, MCP 0.
  4. Accepted deviations: SchemaForm.tsx beyond "error display only": seat-approved before code (one visibility rule, not a copy). The edit page also holds an existing item whose required row is empty; the author can fill it, and that is the card's intent.
  5. Out of scope: none. Sibling Studio: one item toolbar — layers, references, history with rollback, audit, source — on every pillar item #28239 (PR feat(app-shell): Studio item toolbar — layers, references, history with rollback, audit and source on every pillar item #28296) also edits ResourceEditPage.tsx in disjoint regions; the second to land merges origin/main. Skills-docs: none — Metadata-protocol: none.
  6. Landing: check-governed-merges --pr 28293 NOT governed, 431 changed lines ≤ 3000; no contract review owed (Clause-②: no, no packages/spec path); changeset present. The seat readies it and arms auto-merge under the maintainer's 「按 pm-dispatch 审过的pr你可以自主落地」.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

1 participant