|
| 1 | +--- |
| 2 | +'@objectstack/lint': minor |
| 3 | +--- |
| 4 | + |
| 5 | +fix(lint)!: `object-field-ref-unknown` judges the field-name lists on a field — `relatedListColumns`, `lookupColumns`, `lookupFilters[].field`, `dependsOn` — and an object's `indexes[].fields` |
| 6 | + |
| 7 | +<!-- adr-0087: not-required (no-migration-prescription) an authoring-time lint rule judges names on five more declaration positions; no key, symbol, enum member or stored value moves, so a stored metadata row is structurally identical before and after and `objectstack migrate meta` has nothing to rewrite --> |
| 8 | + |
| 9 | +**BREAKING** in the accept-set sense — a declaration that passes today can fail tomorrow. |
| 10 | +Landing in the launch window as `minor` (the lockstep convention: `major` is refused by |
| 11 | +`check-changeset-no-major`, and breaking-ness is carried by this banner plus the ADR-0087 |
| 12 | +disposition above). |
| 13 | + |
| 14 | +**Clause-②: no (narrowing)** — the rule refuses more than it did; no key is added to any |
| 15 | +published payload and no public surface grows. Narrowing is still a semantic-surface change, |
| 16 | +which is why it is declared here rather than shipped silently. |
| 17 | + |
| 18 | +Each of these five lists holds bare field names that the schema cannot judge, and until now no |
| 19 | +authoring door read them for existence, so a misspelling surfaced only when a user opened the |
| 20 | +view or the picker — or never: |
| 21 | + |
| 22 | +- a misspelt `relatedListColumns` entry asked the child object for a column it does not have, |
| 23 | + when the parent's detail page opened; |
| 24 | +- a misspelt `lookupColumns` entry rendered an empty picker column; |
| 25 | +- a misspelt `lookupFilters[].field` filtered the picker's query by a field the referenced |
| 26 | + object lacks; |
| 27 | +- a misspelt `dependsOn` name kept its field gated for good; |
| 28 | +- a misspelt `indexes[].fields` column made the SQL driver skip the WHOLE index at sync, with a |
| 29 | + warning, and drift dropped it too — so a `unique` index was silently unenforced while |
| 30 | + everything looked normal. |
| 31 | + |
| 32 | +`os validate`, `os build` and `os lint` now refuse each of them at `error` (exit 1), under the |
| 33 | +existing rule id `object-field-ref-unknown`, and so does the runtime publish door on an object |
| 34 | +write (`422`), exactly as they already did for `highlightFields` and |
| 35 | +`publicSharing.redactFields`. The finding sits at the exact path — |
| 36 | +`objects[i].fields.<field>.lookupColumns[j].field`, `objects[i].indexes[j].fields[k]`, and so |
| 37 | +on — names the string that was written and the object it was judged against, offers the |
| 38 | +nearest name when one is close, and lists that object's fields. |
| 39 | + |
| 40 | +**Which object a name is judged against** — read off each key's runtime reader, not assumed: |
| 41 | + |
| 42 | +| Position | Judged against | |
| 43 | +|:---|:---| |
| 44 | +| `relatedListColumns[]` | the object that owns the field — the related list shows that (child) object's rows | |
| 45 | +| `lookupColumns[]`, both arms | the referenced object — the picker lists its records | |
| 46 | +| `lookupFilters[].field` | the referenced object — the picker's query runs on it | |
| 47 | +| `dependsOn[]` name, or `{ field }` | the object that owns the field — the form gate reads this record | |
| 48 | +| `dependsOn[]` `param` (or the bare name, on a picker) | the referenced object — the picker filters its candidates by that key | |
| 49 | +| `indexes[].fields[]` | the object itself, including the columns the platform injects (`created_at`, `organization_id`, …) | |
| 50 | + |
| 51 | +The referenced-object positions are judged on `lookup`, `master_detail` and `user` fields (a |
| 52 | +`user` field references `sys_user`), and only when the referenced object is in the stack being |
| 53 | +checked. `lookupColumns`, `dependsOn` and index columns are read verbatim by their readers, so a |
| 54 | +dotted name there is refused as a name that is not a field. The family's three skips hold |
| 55 | +unchanged: an object outside the stack, an object with no readable field map (ADR-0015 |
| 56 | +`external`), and a registry-injected column resolved per object. |
| 57 | + |
| 58 | +**What an author does.** Nothing is renamed or rewritten for you. Fix the name the finding |
| 59 | +points at, or drop the entry. On a lookup whose `dependsOn` field is spelled differently on the |
| 60 | +two records, write the entry with its `param` naming the referenced object's field. An existing |
| 61 | +object carrying one of these misspellings is refused when it is next republished through the |
| 62 | +publish door, and `os validate` reports it on the next run. |
| 63 | + |
| 64 | +Unchanged: the object schema's own parse still admits these names, so a draft save does not |
| 65 | +judge them. An index column that resolves to a real but virtual field (a `formula`) passes this |
| 66 | +rule; whether the column is materialized stays the SQL driver's question at sync. |
0 commit comments