From 387df3e81891bdf28f85b9480d64694cbf741b26 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 05:53:32 +0000 Subject: [PATCH] fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `relationship/delete-behavior` suggestion in `lintDataModel` told an author an undeclared `master_detail.deleteBehavior` could be `cascade`, `restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail` field is a named parse-time rejection, so following the suggestion literally walked an author into a publish-time error. The message now enumerates only the two values FieldSchema actually accepts on a master_detail (cascade/restrict — matching object.form.ts's master_detail deleteBehavior options), and keeps the outcome-naming courtesy of the parse-time rejection message: it still names set_null to say plainly it is not honored on this type, and points to `lookup` for the case where children must survive the parent. The `fix` payload was already correct and is unchanged. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC --- ...lete-behavior-suggestion-drops-set-null.md | 23 +++++++++++++++++++ packages/cli/test/data-model-rules.test.ts | 8 +++++++ packages/lint/src/data-model-rules.ts | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .changeset/lint-delete-behavior-suggestion-drops-set-null.md diff --git a/.changeset/lint-delete-behavior-suggestion-drops-set-null.md b/.changeset/lint-delete-behavior-suggestion-drops-set-null.md new file mode 100644 index 0000000000..749ea7d1c3 --- /dev/null +++ b/.changeset/lint-delete-behavior-suggestion-drops-set-null.md @@ -0,0 +1,23 @@ +--- +"@objectstack/lint": patch +--- + +fix(lint): `relationship/delete-behavior` suggestion no longer names `set_null` as declarable on a `master_detail` + +`lintDataModel`'s `relationship/delete-behavior` suggestion told an author an +undeclared `master_detail.deleteBehavior` could be `cascade`, `restrict`, or +`set_null`. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored +`deleteBehavior: 'set_null'` on a `master_detail` field is a named parse-time +rejection — a detail row cannot outlive its master, so the engine resolves +every value except `restrict` to `cascade` on this type. Following the +suggestion's own `set_null` mention literally walked an author into that +rejection at publish time. + +The message now enumerates only the two values `FieldSchema` actually accepts +on a `master_detail` (`cascade`/`restrict` — matching the vocabulary already +offered by the metadata-admin field form, `object.form.ts`'s `master_detail` +`deleteBehavior` options), and keeps the same outcome-naming courtesy as the +parse-time rejection message: it still names `set_null` to say plainly that it +is not honored on this type, and points to `lookup` for the case where +children must survive the parent. The `fix` payload (`deleteBehavior: +'cascade'`) was already correct and is unchanged. diff --git a/packages/cli/test/data-model-rules.test.ts b/packages/cli/test/data-model-rules.test.ts index c78d29795b..c6019a3610 100644 --- a/packages/cli/test/data-model-rules.test.ts +++ b/packages/cli/test/data-model-rules.test.ts @@ -40,6 +40,14 @@ describe('lintDataModel — relationships', () => { ]); const db = issues.find((i) => i.rule === 'relationship/delete-behavior'); expect(db?.severity).toBe('suggestion'); + // #9689 (PR #11406) made an authored `deleteBehavior: 'set_null'` on a + // master_detail a named parse-time rejection — the DECLARABLE enumeration + // this suggestion names must not walk an author into that rejection. The + // message may still MENTION `set_null` to explain why it is excluded (the + // same outcome-naming courtesy as the field.zod.ts rejection message), so + // assert the declarable set directly rather than a bare substring-absence. + expect(db?.message).not.toContain('cascade/restrict/set_null'); + expect(db?.message).toContain('(cascade/restrict'); }); it('suggests inlineEdit on master_detail line-item children', () => { diff --git a/packages/lint/src/data-model-rules.ts b/packages/lint/src/data-model-rules.ts index f71636000c..4b3c419928 100644 --- a/packages/lint/src/data-model-rules.ts +++ b/packages/lint/src/data-model-rules.ts @@ -604,7 +604,7 @@ export function lintDataModel(objects: any[]): LintIssue[] { issues.push({ severity: 'suggestion', rule: 'relationship/delete-behavior', - message: `master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)`, + message: `master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)`, path: `${fieldPath}.deleteBehavior`, fix: "deleteBehavior: 'cascade'", });