Filing unassigned — recording, not claiming. Found while implementing #9689 (parse-time rejection of deleteBehavior: 'set_null' authored on a master_detail; PR #11406).
The defect
The field designer form metadata still offers "Set null" as a deleteBehavior choice when the field being edited is a master_detail — a combination #9689 makes a named parse-time rejection:
packages/spec/src/data/object.form.ts:156-160 — the deleteBehavior select declares options: [Set null / Cascade / Restrict] with visibleWhen: "data.type in ['lookup','master_detail']". The option list is shared by both types, so a Studio author editing a master_detail is offered "Set null", picks it, and only learns at publish (422) that the choice was never legal.packages/spec/src/data/field.form.ts:49 — same field, visibleWhen: "data.type == 'lookup' || data.type == 'master_detail'" (no inline options there; whether its option source narrows per type needs checking as part of the fix).
The publish-time rejection is loud and names the remedy, so this is a UX gap rather than a data-loss path — but offering a choice the schema refuses is the declared-vs-enforced shape one seam earlier.
Fix shape — needs a small design decision
The form DSL declares one static options array per select. Narrowing per sibling-type needs one of: per-option visibility (if the DSL supports/should support it), two select declarations with disjoint visibleWhen (works today, duplicates the field), or an option-source hook. Whoever takes this should pick against the form DSL's existing vocabulary rather than extending it for one field.
Refs: #9689 (the ruling and the rejection). The published-skill sibling of this finding is #11409 (governed surface, different landing path).
Generated by Claude Code
Filing unassigned — recording, not claiming. Found while implementing #9689 (parse-time rejection of
deleteBehavior: 'set_null'authored on amaster_detail; PR #11406).The defect
The field designer form metadata still offers "Set null" as a
deleteBehaviorchoice when the field being edited is amaster_detail— a combination #9689 makes a named parse-time rejection:packages/spec/src/data/object.form.ts:156-160— thedeleteBehaviorselect declaresoptions: [Set null / Cascade / Restrict]withvisibleWhen: "data.type in ['lookup','master_detail']". The option list is shared by both types, so a Studio author editing amaster_detailis offered "Set null", picks it, and only learns at publish (422) that the choice was never legal.packages/spec/src/data/field.form.ts:49— same field,visibleWhen: "data.type == 'lookup' || data.type == 'master_detail'"(no inline options there; whether its option source narrows per type needs checking as part of the fix).The publish-time rejection is loud and names the remedy, so this is a UX gap rather than a data-loss path — but offering a choice the schema refuses is the declared-vs-enforced shape one seam earlier.
Fix shape — needs a small design decision
The form DSL declares one static
optionsarray per select. Narrowing per sibling-type needs one of: per-option visibility (if the DSL supports/should support it), two select declarations with disjointvisibleWhen(works today, duplicates the field), or an option-source hook. Whoever takes this should pick against the form DSL's existing vocabulary rather than extending it for one field.Refs: #9689 (the ruling and the rejection). The published-skill sibling of this finding is #11409 (governed surface, different landing path).
Generated by Claude Code