|
| 1 | +--- |
| 2 | +"@objectstack/spec": patch |
| 3 | +--- |
| 4 | + |
| 5 | +docs(spec): a rejection says which shape refused the key, and a select option gets the editability boundary (#8202, #8201) |
| 6 | + |
| 7 | +Two text-face halves finishing the story PR #8199 started. No key is added |
| 8 | +anywhere, every spelling rejected before is rejected after, and everything that |
| 9 | +parsed before parses identically — only the sentence an author reads moves. |
| 10 | + |
| 11 | +## #8202 — per-shape `surface` strings |
| 12 | + |
| 13 | +`FormFieldSchema`, `FormSectionSchema` and `PageComponentSchema` shared one |
| 14 | +surface string, so every rejection opened `Unrecognized key(s) on this view/page |
| 15 | +schema`. Harmless while the three answered a key identically; since #8199 they |
| 16 | +do not: |
| 17 | + |
| 18 | +- on a **field**, `disabled` gets a rename pointer — *Did you mean `disabled` → |
| 19 | + `readonly`?* |
| 20 | +- on a **section** or **page component**, it gets the editability-boundary |
| 21 | + prescription — *write `readonly` on the form field(s) inside instead.* |
| 22 | + |
| 23 | +Those two answers contradict each other by design, and the contradiction only |
| 24 | +reads correctly if the message says which shape the author is on. It now does: |
| 25 | +`this form field` / `this form section` / `this page component`. Per #8199's |
| 26 | +placement rule the strings are filed at the three call sites rather than in the |
| 27 | +shared options table — a table shared by three shapes can no more carry one |
| 28 | +shape's name than it can carry one shape's prescription. The shared table keeps |
| 29 | +the family name, and no live declaration may still report it |
| 30 | +(`alias-integrity.test.ts`). |
| 31 | + |
| 32 | +## #8201 — `SelectOptionSchema` inherits the #7887 ruling |
| 33 | + |
| 34 | +The maintainer ruling of 2026-08-12 (a form section / page component gates |
| 35 | +visibility only; editability lives on fields) was scoped to two shapes, and |
| 36 | +#8199 left the third — a select option — with a bare rejection. It has the |
| 37 | +boundary now, on the ruling's own premise re-measured for this shape rather than |
| 38 | +by analogy: on objectui `origin/main` @ `aca27fa` the object-field pipeline these |
| 39 | +options feed has **zero** per-option `disabled` consumers (`SelectField.tsx:161` |
| 40 | +calls the root-level `disabled` "the single authority"; `RadioField.tsx:124` |
| 41 | +reads only `props.disabled`). The shown-but-unselectable option that does exist |
| 42 | +lives in objectui's SDUI vocabulary, which is not this shape. |
| 43 | + |
| 44 | +Its prescription is **not** the siblings' text, because the siblings' |
| 45 | +destination does not exist here — a section redirects to the fields inside, and |
| 46 | +an option has no inside. Writing `disabled` on an option now points at the two |
| 47 | +things that are real: per-option `visibleWhen` to withdraw that one option (the |
| 48 | +only `*When` surface that binds `current_user`, ADR-0068 — and the rule |
| 49 | +validator refuses a write of a value whose predicate is false), and |
| 50 | +`readonly` / `readonlyWhen` on the **field** to freeze the whole picker. It |
| 51 | +states what the platform honours today; a non-selectable field option remains a |
| 52 | +spec decision someone may ask for, not a key an author writes. |
| 53 | + |
| 54 | +## Why `patch`, measured rather than inherited |
| 55 | + |
| 56 | +The criterion is whether the prose reaches a consumer: |
| 57 | + |
| 58 | +- **Parse-reachable error string — YES.** Both halves are `unrecognized_keys` |
| 59 | + message text an author reads out of a failed parse, and every claim is pinned |
| 60 | + against a real `safeParse` error rather than against the options table. |
| 61 | +- **`dist/**/*.d.ts` hover — YES for #8201.** The boundary paragraph is JSDoc on |
| 62 | + the exported `SelectOptionSchema` const. |
| 63 | +- **Generated reference page — NO, measured.** `content/docs/references/data/` |
| 64 | + `field.mdx` is built from the file-level doc block plus per-property |
| 65 | + `.describe()` text; a schema's own JSDoc is not rendered (grepping the page |
| 66 | + for `SelectOptionSchema`'s existing docblock text returns nothing, while its |
| 67 | + per-key `.describe()` string hits). No `.describe()` was touched, so the page |
| 68 | + and `authorable-surface.base.json` do not move. |
| 69 | + |
| 70 | +One reachable consumer is enough and no public export is added, so `patch`. |
0 commit comments