You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(spec): notNull / not_null prescribe storage.notNull, not required (#17477)
`FieldSchema` refused the flattened column-constraint spellings and then
prescribed `required` — the one key ADR-0113 exists to say is NOT the column
constraint. `required`'s own `.describe()` in the same file says the opposite
of what the rename prescribed: "NOT a column constraint — the physical NOT
NULL is a separate explicit opt-in (`storage.notNull`)".
The refusal was never the problem; the REMEDY was. An author reaching for a
NOT NULL column complied, wrote `required: true`, and got a nullable column
plus a write gate, with nothing downstream to refuse it — the loud failure
resolved into a silent wrong end state.
`notNull: 'required'` leaves the alias table. All three flattened spellings —
`notNull`, `not_null`, `storageNotNull` — are answered by one
`guidanceSets` entry naming `storage: { notNull: true }`, and naming
`required` as the write contract too, since the defect is precisely that the
author cannot tell the two axes apart.
The entry moved to `guidanceSets` rather than exact `guidance` because the
two channels fold differently: `aliases` is indexed by `aliasProbe` (case and
separator folded, so one row also covered `not_null`) while exact `guidance`
is matched case-sensitively. A lone `guidance.notNull` row would have dropped
`not_null` onto the edit-distance fallback.
No accepted key moves: a guidance table decorates a rejection and never
admits a key, so both spellings are refused before and after. `isRequired`
and `mandatory` are genuine write-contract synonyms and still rename.
The stale comment claiming `notNull` "gets its own sentence rather than a
rename" is replaced — it described an arrangement that had not landed.
Fixes#16867
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>
fix(spec): `FieldSchema` no longer prescribes `required` for `notNull` / `not_null` — the flattened column-constraint spellings now name `storage: { notNull: true }` (#16867)
6
+
7
+
Writing `notNull: true` (or `not_null: true`) on a field was refused — correctly — and then told to write `required` instead, via a rename row in `FieldSchema`'s alias table. `required` is the one key ADR-0113 exists to say is **not** the column constraint. `required`'s own description in the same file states the opposite of what the rename prescribed: *"NOT a column constraint — the physical NOT NULL is a separate explicit opt-in (`storage.notNull`)"*.
8
+
9
+
The failure mode was not the refusal — that fired, loudly, and did its job. It was the **remedy**: an author reaching for a NOT NULL column complied, wrote `required: true`, and received a nullable column plus a write-time gate, with nothing downstream to refuse it. The refusal read as though it had been satisfied.
10
+
11
+
All three flattened spellings — `notNull`, `not_null`, and `storageNotNull`, which already carried the correct sentence — now get one prescription naming the real key:
12
+
13
+
> physical column constraints live under `storage` — write `storage: { notNull: true }` (ADR-0113). There is no flat spelling of it: post-17 a column is NOT NULL because its author wrote that nested key, and for no other reason. It is NOT `required`, which is the WRITE contract (an insert must provide a value; an update may not null it out) and deliberately does NOT imply the column constraint — `required: true` alone leaves the column nullable. Write whichever of the two you meant, or both.
14
+
15
+
Both halves are named on purpose: the defect being repaired is that the author cannot tell which of the two axes they are getting, so a prescription naming only the column half would have fixed the measured direction and opened the mirror-image one.
16
+
17
+
**No accepted key moves.**`notNull` and `not_null` were refused before this change and are refused after it — a `guidance` / `guidanceSets` table decorates a rejection and never admits a key. Only the sentence attached to the refusal changed. `storage: { notNull: true }` parsed before and parses now; `isRequired` and `mandatory` are genuine spellings of the write contract, ADR-0113 moved neither, and both still rename onto `required`.
18
+
19
+
One mechanical note for anyone repairing a table like this: the entry moved from `aliases` to `guidanceSets`, not to exact `guidance`. `aliases` is indexed by `aliasProbe` (case- and separator-folded, so one row covered `not_null` too) while exact `guidance` is matched case-sensitively on the authored spelling — a lone `guidance.notNull` row would have quietly dropped `not_null` onto the edit-distance fallback. The two spellings are pinned separately for exactly that reason.
requiredWhen: ExpressionInputSchema.optional().describe('Predicate (CEL) — the cell is required when TRUE. Same `record` + `parent` scope as `readonlyWhen`. PRESENTATION ONLY: this flags the cell inline-invalid in the grid; nothing on the write path reads it. The server-enforced contract is the child FIELD\'s own `requiredWhen` — a transition gate, see `Field.requiredWhen` — which hydration copies onto an identity-only column, so declaring the requirement here alone enforces nothing.'),
847
848
}));
848
849
850
+
/**
851
+
* The FLATTENED spellings of the column constraint — `notNull`, `not_null`,
852
+
* `storageNotNull` — answered with one prescription that names the real key.
853
+
*
854
+
* ## Why this is prose and not a rename
855
+
*
856
+
* ADR-0113 split one knob into two axes, and adjudicated the spelling of each:
857
+
* `required` is the write-time contract, and the physical constraint is
858
+
* `storage: { notNull: true }` (Q1, decided 2026-07-30). The target is
859
+
* therefore a NESTED key, and `aliases` renames onto a flat one — the same
860
+
* reason `currency` is answered in prose a few lines below.
861
+
*
862
+
* ## Why it may not rename onto `required` (#16867)
863
+
*
864
+
* It used to: `notNull: 'required'` sat in the alias table beside `isRequired`
865
+
* and `mandatory`, and because `aliases` is consulted only AFTER this channel
866
+
* declines, an author who wrote `notNull` was told to write the one key ADR-0113
867
+
* exists to say is not the column constraint. `required`'s own `.describe()`
868
+
* below states the opposite in the same file: *"NOT a column constraint — the
869
+
* physical NOT NULL is a separate explicit opt-in (`storage.notNull`)"*.
870
+
*
871
+
* The cost of that rename was not a wording nit. The refusal was loud and did
872
+
* its job; its REMEDY produced the wrong end state, and that end state was
873
+
* SILENT — the author complied, got `required: true`, and received a nullable
874
+
* column plus a write gate with nothing downstream to refuse it. The same
875
+
* conflation was withdrawn from the conversion registry on the same reading
0 commit comments