Repository navigation
Six hand-written pages document set_null without the multi-value semantics that #9438 settled — remove-the-member, emptied set reads back as [] #9521
Copy link
Copy link
Labels
documentationImprovements or additions to documentationImprovements or additions to documentationdomain:devxpm:dispatched
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentation
on Aug 18, 2026 os-support-ai commented
on Aug 18, 2026 CollaboratorMore actionsFirst-touch grading (triage seat): promoted
finding→pm:queue, routeddomain:devx(all six files arecontent/docs/**hand-written pages), typeTask.The semantics are permanent (ruled on #9447, contract landed in
field.zod.tsvia #9493, engine landed via #9520), so the docs gap is real now and only ages. The card already carries the discipline the claimant needs: per-page judgement rather than a blanket edit, citepackages/spec/src/data/field.zod.tsrather than paraphrase,releases/v15.mdxis ⛔ read-only, and thevalidation-rules.mdxhalf must not promise the #9476 enforcement that isn't live yet.Size/model suggestion: S–M (six pages to judge, likely 2–3 needing real sentences).
Generated by Claude Code
- added a commit that references this issue
on Aug 18, 2026 { "issue": 9521, "status": "done", "branch": "claude/issue-9521-set-null-multi-semantics", "pr": "https://github.com/objectstack-ai/objectstack/pull/9627", "premise_still_valid": true, "summary": "All six pages edited, each judged on its own job rather than given one pasted block. Classification (PM's untested assumption): exactly ONE was wrong — protocol/objectql/types.mdx stated the single-value behaviour as universal ('set_null: Set field to null'); one was imprecise — data-modeling/fields.mdx listed null / empty-string / [] as interchangeably 'exactly what set_null writes', true of none of them individually; the other four were silent, and their silences differ, so their fixes do: field-types.mdx got one clause in each of the two table cells where an author picks deleteBehavior and multiple; troubleshooting.mdx got the answer in place for the mid-incident reader (a cleanup query looking for null will not match an emptied set); data-api.mdx got the read-back shape on DELETE /data/:object/:id, where an integrator's client branches; validation-rules.mdx got the live representation guarantee plus the ruled required-means-non-empty half marked DECLARED BUT NOT YET ENFORCED (validator isMissing treats only undefined/null/blank-string as missing, so [] passes today — #9476), so the page promises nothing the runtime lacks. Contract quoted from packages/spec/src/data/field.zod.ts (the multiple and required doc blocks), and every page cites it via the generated /docs/references/data/field page, which renders those exact describe strings — a pointer that cannot drift instead of a seventh restatement. One correction to the card's paraphrase: the member-removal half is NOT in the spec's deleteBehavior describe (that one still reads 'What happens if referenced record is deleted'); it is stated in the multiple doc block's parenthetical and in cascadeDeleteRelations (engine.ts:10206-10212), which is what I cited for it. SURVEY (six is the right edit set, derived independently, not inherited): grep of content/docs for set_null gives 8 files — the six plus references/data/field.mdx (AUTO-GENERATED from field.zod.ts, already carries both sentences verbatim, spec is its fix site) and releases/v15.mdx (release-owned, untouched); grep for deleteBehavior finds 2 further hand-written pages the set_null literal key cannot see (getting-started/common-patterns.mdx, protocol/objectql/schema.mdx) — both silent, neither states an empty representation, so neither needed an edit, but the key would have missed them had they been wrong; grep for multiple:true / multi-value finds 5 more pages, none making a null/empty claim. Judgment PM may want to check: writing the not-yet-enforced required rule on validation-rules.mdx WITH an explicit 'declared but not yet enforced (#9476)' warning, rather than staying silent about it.", "tests": "All at the final commit 496b3de13 (git rev-parse --short HEAD after the last commit; union re-run on that tree). pnpm check:nul-bytes -> 'check-nul-bytes: OK (scanned 6173 text file(s) ... no raw ASCII control bytes)'. pnpm check:docs-audit-scope -> 'docs-accuracy-audit scope is in sync with content/docs/: 179 hand-written doc(s)' + 'release-owned pages are in scope and read-only: 9 page(s)'. pnpm check:docs-redirects -> 'OK (apps/docs/redirects.mjs: 92 entries ...)'. pnpm check:role-word -> 'OK (43 baselined file(s), no new occurrences)'. Spec liveness family (dispatch-gates derived from my actual changed paths, run under flock /tmp/os-heavy-verify.lock): check:empty-state rc=0 'all classified (1 closed, 2 open, 4 output, 9 scope)'; check:liveness rc=0; check:strictness-ledger rc=0; check:variant-docs rc=0 '18 discriminated union(s) — 8 governed, 10 exempt'. No local gate parses MDX, so all six pages were compiled through @mdx-js/mdx 3.1.1 directly: 6/6 OK, with a POSITIVE CONTROL (a deliberately unclosed component tag) observed FAILING 'Expected a closing tag ... before the end of paragraph', rc=1 — so the harness is not silently green. No unit tests apply (docs-only). CI snapshot at report time on 496b3de13: 24 check runs — 11 success, 9 skipped, 4 in_progress, 0 failure; CI convergence is the PM's half.", "open_questions": [], "out_of_scope_findings": [ "filed as #9625: types.mdx claims an explicit deleteBehavior:'set_null' is 'always honored as written', but cascadeDeleteRelations escalates on the RESOLVED behaviour (engine.ts:10265-10281) so it cannot distinguish an explicit set_null from a defaulted one on a required lookup; no fixture pins either reading, and the same branch refuses a delete on a required multiple:true lookup even when member removal would leave the set non-empty, which the #9447 ruling accepts. Different defect class and the correct form is a decision, so not fixed in this PR." ] }
Generated by Claude Code
- added a commit that references this issue
on Aug 23, 2026
Metadata
Metadata
Assignees
Labels
documentationImprovements or additions to documentationImprovements or additions to documentationdomain:devxpm:dispatched
Filing unassigned — recording, not claiming. Surfaced by the
Docs Drift Checkon PR #9520 (card #9438) and deliberately not fixed there, for the reasons below.The gap
deleteBehavior: 'set_null'on amultiple: truereference field now has settled, permanent semantics:[], nevernull;requiredon 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(themultipleandrequireddoc blocks) via #9493 (df0c12de7), and implemented in the engine by #9520 (card #9438).None of the six hand-written pages that document
set_nullsays any of it. Each was flagged by the drift check via theset_nullliteral:content/docs/api/data-api.mdxcontent/docs/data-modeling/field-types.mdxcontent/docs/data-modeling/fields.mdxcontent/docs/data-modeling/validation-rules.mdxcontent/docs/deployment/troubleshooting.mdxcontent/docs/protocol/objectql/types.mdx⛔
content/docs/releases/v15.mdxalso namesset_nulland 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:
restrictescalation 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."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.mdxandfield-types.mdxare the likeliest to owe a real sentence, since they documentdeleteBehaviorwhere an author chooses it.protocol/objectql/types.mdxis protocol-level and may want the residual-shape guarantee stated precisely.api/data-api.mdx,validation-rules.mdxanddeployment/troubleshooting.mdxmay only mentionset_nullin passing — check before writing.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
[]satisfiesrequiredon amultiple: truelookup — diverges from the #9447 ruling (required means non-empty array) #9476 — the ruledrequired-means-non-empty semantics are documented but not yet enforced: the validator currently passes[]on arequired+multiple: truelookup (record-validator.ts:171-173; read sites:477/:1011). Anyone writing thevalidation-rules.mdxhalf should know the enforcement is pending, so the page does not promise behaviour the runtime does not yet have.[]ornull? #9447 (the ruling), docs(spec): FieldSchema pins the ruled multi-value empty representation ([] + required means non-empty) (#9447) #9493 (the landed contract), cascadeDeleteRelations' set_null limb nulls the WHOLE multi-value array, dropping every other live reference #9438 / 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 (the engine fix), fix(engine): probe a multiple:true reference field with a spelling its storage answers #9437 (the interim, now reverted).