Skip to content

Studio metadata forms: an optional enum select has no empty item, so a chosen deleteBehavior on a user field cannot be cleared #29733

Closed

Description

@objectstack-fleet

domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz

Filing gate: ① a product defect with a named site, found by the contract review of PR #29712 (card #29415, record 6594347454, should-fix 2). ⛔ Not a claim. No grade: triage sets it.

What happens

ui/packages/app-shell/src/views/metadata-admin/SchemaForm.tsx renders an enum-backed row as a Select whose value is value == null ? '' : String(value) (≈:2393 and ≈:2421 on origin/main), and its item list carries only the enum's values. There is no item that sets the key back to absent. So once an author picks a value in an optional enum row, the form cannot return it to "declares nothing".

For most rows that is a cosmetic gap. For the deleteBehavior row it is not: PR #29712 shows the row on a user field (field.form.ts / object.form.ts, data.type in ['lookup','user']), and on a user field "declares no deleteBehavior" is a distinct, enforced outcome (the id is kept and the field stays out of the delete walk, #29253 / PR #29368). An author who tries a value and changes their mind leaves set_null, cascade or restrict in force with no way back from Studio.

Reach

Read from the code, not yet measured at the door. The fixing seat measures it first: open a user field in Studio, pick a deleteBehavior, save, then try to return the row to unset.

Business reason

A metadata author (human or AI) using Studio can turn on cascade or restrict deletes on user references and cannot turn them off without editing raw metadata. Not fixing it leaves an authoring trap on a delete-semantics key.

Reader

domain:ui, the seat taking this from pm:queue: an empty / "not set" item for optional enum rows in the metadata-admin SchemaForm select widgets (both select paths), writing the key as absent.

Dedupe

gh api search/issues on repo:objectstack-ai/objectstack is:issue, open and closed:

Links back: #29415, PR #29712, #29253, PR #29368.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 · domain:ui · pm:queue · 2026-11-03T03:55Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ

    Claim: PM loop round 11 · 2026-11-03T04:07Z
    Session: session_01QsR2jUDMc41NruhHkjaN5X (card thread, os-dev); dispatched by seat session session_01QsR2jRFuJz9QBZqPVHYVhJ
    Account: zhuangjianguo
    Branch: claude/project-thread-pcbqof (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-29733
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: ui/packages/app-shell/src/views/metadata-admin/SchemaForm.tsx (the optional enum select paths only), a "not set" i18n key only if none exists (all locales, zh census), tests, one changeset. Stop on breach; explain in the report
    Container & model: S, mode:thread, model: default tier (dispatch-gates --tier on the final file list), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: an optional enum row in a Studio metadata form has no empty item | a picked deleteBehavior on a user field cannot be returned to unset from Studio | metadata authors (human or AI) editing fields in Studio
    Thread-read: none
    Premise: the filer's code reading on main (SchemaForm Select items carry only the enum values), not measured; the os-dev measures the user field deleteBehavior case live before code.
    Serial constraints cleared: none open on SchemaForm.tsx's select paths known to this seat; the os-dev checks open PRs touching the file and merges main before pr_create

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ

    Claim amendment (to 6595203212) · 2026-11-03T04:38Z · on the os-dev's measure; session, branch and seat unchanged.

    Clause-②: no
    Metadata-protocol: none

    • Measured: the Studio field panel (ObjectFieldInspector) offers no deleteBehavior row on a user or lookup field, so the card's named consequence is not reachable from Studio today; the seat files that as its own finding. The defect itself is live: the "More fields" fallback form's optional enum openIn (actions) offered only self / new-tab.
    • Second cause found: the five DefaultInspector fallback forms (Action, Dashboard, Hook, Page, Report) hand SchemaForm's next value to an onPatch that merges, so a key SchemaForm removes survives in the draft. A "None" item alone would be a dead control there (clearing a text box there is lost the same way).
    • File surface (added): those five inspector call sites (onChange only) and one shared pure helper that returns removed keys as explicit undefined, so the merge drops them. onPatch's merge is unchanged. Still SchemaForm.tsx (both optional enum select paths, a "None" item from the existing engine.form.none key, no new i18n key), tests, one changeset.
  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · zhuangjianguo · session_01QsR2jUDMc41NruhHkjaN5X
    os-dev-report

    {
      "issue": 29733,
      "status": "done",
      "branch": "claude/project-thread-pcbqof",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29805",
      "head": "74e47d1e",
      "session": "session_01QsR2jUDMc41NruhHkjaN5X",
      "premise_still_valid": true,
      "premise_note": "The SchemaForm defect is real on both select paths. The card's named door (deleteBehavior on a user field in Studio) is NOT reachable: ObjectFieldInspector shows no Delete Behavior row on user or lookup fields (measured live).",
      "summary": "SchemaForm's two enum select paths (form options, schema enum) now lead an optional row with a None item (existing engine.form.none key, sentinel mapped to onChange(undefined)); required rows and scalar list elements unchanged. Per amendment 6595600429, the five DefaultInspector fallback forms (Action, Dashboard, Hook, Page, Report) route onChange through a pure fallbackFormPatch helper so keys SchemaForm deletes reach the merging onPatch as explicit undefined; this also fixes emptied text/number boxes there. Live: action openIn None, save, publish, re-read has no openIn.",
      "files_changed": [
        "ui/packages/app-shell/src/views/metadata-admin/SchemaForm.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/SchemaForm.optionalEnumUnset-29733.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/fallbackFormPatch.ts",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/fallbackFormPatch-29733.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/ActionDefaultInspector.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/DashboardDefaultInspector.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/HookDefaultInspector.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/PageDefaultInspector.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx",
        ".changeset/29733-optional-enum-unset.md"
      ],
      "line_budget": "520 changed lines vs merge base before the main merge (+512 / -8), under 3000; tier: no path-derived mandate",
      "tests": "New: SchemaForm.optionalEnumUnset-29733 8/8, fallbackFormPatch-29733 4/4 (rerun on 74e47d1e: 12/12). Ablation A1 offerUnset forced false: 4 of 8 red, 4 controls green. Ablation A2 helper undefined line removed: 3 of 4 red, control green. Both via ablation-replace.mjs, restored to HEAD blob, git diff HEAD empty. Touched suites (144 ui test files naming SchemaForm, the five inspectors or engine.form.none): 1464 tests green in 3 locked chunks (52/44/48 files). app-shell type-check exit 0; eslint on 9 touched files 0 errors.",
      "gates": "dispatch-gates --commands on 74e47d1e: 35 derived, all exit 0 (type-check-debt and ui vitest scripts under the verify lock); --ran: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. check-sdui-manifest exit 0. Body gates: check-partof-closing-keyword exit 0, check-closing-keyword-parity --body binds only 29733, changeset-no-major and adr-0087 exit 0 on the simulated event. PR check-runs: in_progress at report time.",
      "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
      "deviations": [
        "Surface widened by claim amendment 6595600429 (five inspector onChange call sites + one helper), approved by the seat before the edit.",
        "Live before/after is action openIn in the More fields fallback form, not the user-field deleteBehavior case: that door does not exist in Studio (seat-approved).",
        "Main merged at 25450217; 11 later main commits touch none of the PR's files, so no second merge before pr_create.",
        "PR not assigned and no reviewers requested, per seat common rules."
      ],
      "mcp_calls": "5 reads: issue_read x3 (get, get_comments x2), list_pull_requests x1; no MCP writes",
      "api_writes": "1: fleet-write relay pr_create (repository_dispatch, run 41585020693) -> PR 29805; plus this post-stamped comment",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: a (UX/authoring dead end) · reach: Studio object designer, user field assignee and lookup field parent_ticket selected, field panel lists no Delete Behavior row (screenshots issue-29733/before-user-field-panel-no-deleteBehavior.png, lookup-field-panel-no-deleteBehavior.png) · evidence: ObjectFieldInspector never names deleteBehavior; object.form.ts rows are SchemaForm-only and the object editor renders the designer · dedupe: deleteBehavior Studio field panel, ObjectFieldInspector delete behavior, lookup cascade designer · carrier: seat files",
        "observation · the action inspector's curated variant select (InspectorSelectField) offers no unset item once picked · carrier: none, noted in the PR acceptance notes"
      ]
    }
  5. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ

    Landed · 2026-11-03T07:04Z · PR #29805 merged through the queue as 5ac50e48. The merge commit carries all 10 files of the accepted head 74e47d1e: SchemaForm.tsx, inspectors/fallbackFormPatch.ts, the five DefaultInspector onChange lines, two test files, and the changeset. Delivered in full: an optional enum row in the Studio metadata forms offers "— None —", which writes the key as absent, and clears in the five inspectors' "More fields" forms reach the draft. Closed by the PR's Fixes; pm:dispatched removed. Filed: #29807 (the designer field panel has no Delete Behavior row).

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions