Skip to content

Six hand-written pages document set_null without the multi-value semantics that #9438 settled — remove-the-member, emptied set reads back as [] #9521

Description

@os-zhuang

Filing unassigned — recording, not claiming. Surfaced by the Docs Drift Check on PR #9520 (card #9438) and deliberately not fixed there, for the reasons below.

The gap

deleteBehavior: 'set_null' on a multiple: true reference field now has settled, permanent semantics:

  • the deleted member is removed from the stored array; the rest are kept;
  • an emptied set reads back as [], never null;
  • required on a multi-value lookup means non-empty array.

Ruled by the maintainer on #9447 (2026-08-18), landed as verbatim contract in packages/spec/src/data/field.zod.ts (the multiple and required doc blocks) via #9493 (df0c12de7), and implemented in the engine by #9520 (card #9438).

None of the six hand-written pages that document set_null says any of it. Each was flagged by the drift check via the set_null literal:

  • content/docs/api/data-api.mdx
  • content/docs/data-modeling/field-types.mdx
  • content/docs/data-modeling/fields.mdx
  • content/docs/data-modeling/validation-rules.mdx
  • content/docs/deployment/troubleshooting.mdx
  • content/docs/protocol/objectql/types.mdx

content/docs/releases/v15.mdx also names set_null and is release-owned / read-only — it is listed here only so nobody "helpfully" edits it. If it is factually wrong, that is a separate docs-only PR, never a rider.

Why it wasn't fixed in #9520

Two rulings, both mine as PM, recorded so they aren't re-litigated:

  1. On fix(engine): probe a multiple:true reference field with a spelling its storage answers #9437 (the interim holding position) I ruled no doc edits, because that PR shipped a deliberately temporary restrict escalation designed to be deleted in one line. Six pages describing a temporary refusal would have had to be unwritten a few hours later. I said then that the docs question "becomes real and permanent when cascadeDeleteRelations' set_null limb nulls the WHOLE multi-value array, dropping every other live reference #9438 answers the semantics; it should be settled there, once."
  2. On fix(engine): set_null on a multiple:true reference removes the deleted member; emptied set stored as [], never null; drop the #9437 interim 409 hold #9520 — that moment — I ruled still not in the code PR. The pages span surfaces that diff does not otherwise touch, the contract sentence already landed where it belongs (FieldSchema), and doc churn riding a code change is how the release-notes guardrail got written in the first place. But the gap is real now that the semantics are permanent, so it gets a card rather than evaporating.

Not a blanket "update six pages"

Each page needs judging on its own — some may only need a clause, some may need nothing:

  • data-modeling/fields.mdx and field-types.mdx are the likeliest to owe a real sentence, since they document deleteBehavior where an author chooses it.
  • protocol/objectql/types.mdx is protocol-level and may want the residual-shape guarantee stated precisely.
  • api/data-api.mdx, validation-rules.mdx and deployment/troubleshooting.mdx may only mention set_null in passing — check before writing.

⚠️Cite packages/spec/src/data/field.zod.ts, do not paraphrase it. The spec seat owns that sentence; #9520's engine change consumes it by citation precisely so there is one source of truth. A hand-written page that restates the contract in its own words becomes a second one that can drift with nothing to catch it.

Related and adjacent

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationfinding

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions