diff --git a/.changeset/30893-bare-field-ref-symptom.md b/.changeset/30893-bare-field-ref-symptom.md new file mode 100644 index 00000000000..6c2b76d10ab --- /dev/null +++ b/.changeset/30893-bare-field-ref-symptom.md @@ -0,0 +1,10 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): three skills describe a bare field ref in a predicate as the fault it is today, not a silent `null` + +Clause-②: no +Metadata-protocol: none + +`objectstack-platform/references/operations.md`, `objectstack-data` and `objectstack-ui` said a bare field ref (`done` instead of `record.done`) "evaluates to `null`" and silently hides or never fires. It faults. On the server an `error` validation rule and a `requiredWhen` refuse the write and name the rule, an option `visibleWhen` refuses only a write that picks that option (forms still offer it), a `readonlyWhen` field stays editable on the form while the server drops the change with a warning and the save succeeds, and a formula reads `null` with a logged warning. In the console an action's `visible` hides it, its `disabled` greys it out, and a field `visibleWhen` shows the field, each with a console warning. The data skill no longer lists RLS / sharing predicates here: their compiler reads a bare name as the column. The prevention advice (`os validate` / `os build`, `record.`) is unchanged. Growth is paid by same-file deletions of restated prose. diff --git a/skills/objectstack-data/SKILL.md b/skills/objectstack-data/SKILL.md index 64855998ebc..c75cdee23fc 100644 --- a/skills/objectstack-data/SKILL.md +++ b/skills/objectstack-data/SKILL.md @@ -202,7 +202,7 @@ export const Invoice = ObjectSchema.create({ set → field permission `when`, rules/security.md). - **`readonly: true` governs the end-user surface, not trusted system writers.** A non-system write (REST/UI, and any `runAs:'user'` flow — the default) has - the field **stripped** from any non-system write; the write reports + the field **stripped**; the write reports success but the value never lands. System-context writes — `runAs:'system'` flows, system hooks, seeds, imports, migrations — are exempt and DO write it. So the pattern "users can't edit this, but automation @@ -447,13 +447,15 @@ os validate # Zod schema + CEL predicates (record. existence) + bindi # or: os build # the same gates, plus emits dist/ ``` -It catches what otherwise fails **silently at runtime**: a bare field ref in a -`requiredWhen` / `readonlyWhen` / `visibleWhen`, a validation rule, a formula, or -a row-level-security/sharing predicate (`done` instead of `record.done`) that -evaluates to `null` and never fires. `os lint` is a *separate* +It catches what otherwise surfaces only at runtime, where a bare field ref +(`done` for `record.done`) faults: an `error` validation rule or `requiredWhen` +refuses the write naming it, an option `visibleWhen` only a write picking the +option (forms still offer it); a `readonlyWhen` field stays editable and the +save succeeds without its change (logged); a formula reads `null` (logged). +`os lint` is a *separate* pass that additionally checks the data model against the conventions in this -skill (relationships, master-detail, roll-ups) — run it too, but it does **not** -replace `os validate`. In a scaffolded project the gate is `npm run validate`. +skill (relationships, master-detail, roll-ups) — run it too. +In a scaffolded project the gate is `npm run validate`. --- diff --git a/skills/objectstack-platform/references/operations.md b/skills/objectstack-platform/references/operations.md index 57944ff920b..40fc22b0a56 100644 --- a/skills/objectstack-platform/references/operations.md +++ b/skills/objectstack-platform/references/operations.md @@ -24,11 +24,11 @@ ## Verify your work -ObjectStack metadata mistakes fail **silently at runtime**, not at edit time: -a bare field ref in a predicate (`done` instead of `record.done`) evaluates to -`null` and silently hides an action/validation on every record; a -dangling dashboard widget binding renders an empty chart (ADR-0021). Both are -caught at author time by one command: +Metadata mistakes surface at runtime, not at edit time: a bare field ref in a +predicate (`done` for `record.done`) faults, so an `error` validation rule +refuses each write naming the rule and an action's `visible` hides it; a +dangling dashboard widget binding renders an empty chart. Both are caught at +author time by one command: ```bash os validate # Zod schema + CEL predicates + widget bindings — no artifact diff --git a/skills/objectstack-ui/SKILL.md b/skills/objectstack-ui/SKILL.md index eee8e3ad2bf..3fb5925c874 100644 --- a/skills/objectstack-ui/SKILL.md +++ b/skills/objectstack-ui/SKILL.md @@ -264,18 +264,17 @@ os validate # CEL predicates (record.) + widget bindings + schema # or: os build # the same gates, plus emits dist/ ``` -Two UI-specific traps it catches, both **silent at runtime** otherwise: +Two UI-specific traps it catches, otherwise met only at runtime: - **Action / field predicate** — a bare field ref in an action `visible` / `disabled` or a field `visibleWhen` (`done` instead of `record.done`) - evaluates to `null` and hides the control on *every* record (the - "button never shows" trap). + faults on *every* record: the action hides (`visible`) or greys out + (`disabled`), the field shows; the console logs the fault. - **Dashboard widget binding** — a widget `dataset` / `dimensions` / `values` that doesn't resolve to a declared dataset/field renders an empty chart (ADR-0021). -Don't report a view/action/dashboard done until `os validate` passes. In a -scaffolded project the gate is `npm run validate`. +In a scaffolded project the gate is `npm run validate`. ---