From 87e83df5bcfd952af0d57ff54817de5560557c98 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Nov 2026 18:00:28 +0000 Subject: [PATCH 1/2] docs(skills): a bare field ref in a predicate faults, it does not read null Three skills said a bare field ref (`done` instead of `record.done`) evaluates to null and silently hides an action or never fires a rule. On main it faults: an error validation rule, requiredWhen and an option visibleWhen refuse the write naming the rule, readonlyWhen holds its field locked, a formula reads null with a warning, and in the console an action's visible hides it, disabled greys it out and a field visibleWhen shows the field, each with a console warning. RLS / sharing predicates read a bare name as the column, so the data skill no longer lists them. Growth is paid by deleting restated prose in the same files. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01J8VVAjPD6BESVmRJphXXyw --- .changeset/30893-bare-field-ref-symptom.md | 10 ++++++++++ skills/objectstack-data/SKILL.md | 13 +++++++------ .../objectstack-platform/references/operations.md | 10 +++++----- skills/objectstack-ui/SKILL.md | 9 ++++----- 4 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 .changeset/30893-bare-field-ref-symptom.md diff --git a/.changeset/30893-bare-field-ref-symptom.md b/.changeset/30893-bare-field-ref-symptom.md new file mode 100644 index 00000000000..ae1c66a9930 --- /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, a `requiredWhen` and an option `visibleWhen` refuse the write and name the rule, a `readonlyWhen` holds its field locked, 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 ccadff8e984..93c94941e8e 100644 --- a/skills/objectstack-data/SKILL.md +++ b/skills/objectstack-data/SKILL.md @@ -447,13 +447,14 @@ 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, `requiredWhen` +or option `visibleWhen` refuses the write naming it, a `readonlyWhen` locks +its field, a formula reads `null` (logged), a form shows a `visibleWhen` field. +`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`. --- From a7da76162bcef12b7901e16974df6d3f7be025e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Nov 2026 18:50:28 +0000 Subject: [PATCH 2/2] docs(skills): name what an option visibleWhen refuses and what a readonlyWhen does on the form An option visibleWhen that faults refuses only a write that picks that option, and forms keep offering it. A readonlyWhen that faults on a bare ref keeps the field editable on the form, the save succeeds, and the server drops the change with a warning. Paid by dropping the form-side visibleWhen clause (the ui skill states it) and a self-restating "from any non-system write" in the readonly bullet. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01J8VVAjPD6BESVmRJphXXyw --- .changeset/30893-bare-field-ref-symptom.md | 2 +- skills/objectstack-data/SKILL.md | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.changeset/30893-bare-field-ref-symptom.md b/.changeset/30893-bare-field-ref-symptom.md index ae1c66a9930..6c2b76d10ab 100644 --- a/.changeset/30893-bare-field-ref-symptom.md +++ b/.changeset/30893-bare-field-ref-symptom.md @@ -7,4 +7,4 @@ docs(skills): three skills describe a bare field ref in a predicate as the fault 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, a `requiredWhen` and an option `visibleWhen` refuse the write and name the rule, a `readonlyWhen` holds its field locked, 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. +`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 4a978d3ff29..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 @@ -448,9 +448,10 @@ os validate # Zod schema + CEL predicates (record. existence) + bindi ``` It catches what otherwise surfaces only at runtime, where a bare field ref -(`done` for `record.done`) faults: an `error` validation rule, `requiredWhen` -or option `visibleWhen` refuses the write naming it, a `readonlyWhen` locks -its field, a formula reads `null` (logged), a form shows a `visibleWhen` field. +(`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.