Skip to content

Commit b480011

Browse files
os-zhuangclaude
andauthored
docs(data-modeling): document build-time expression validation guardrails (#3201)
The formula/flow guardrail series (#1928) added build-time checks that were never documented. Add a "Build-time validation" section to the formulas guide covering: qualify field references (bare-ref → null), field existence, unknown functions, and the text/boolean-in-arithmetic type-soundness warning — with their severities (error vs advisory warning), the note that integer-literal arithmetic (amount / 100) works at runtime, and how flow conditions differ (bare fields, did-you-mean typo warnings). Add a companion note + cross-link to the validation guide. Docs-only; verified against the shipped behavior in @objectstack/formula and @objectstack/lint. Claude-Session: https://claude.ai/code/session_01Hnji7EEYR2mGt6pY53a8Bm Co-authored-by: Claude <noreply@anthropic.com>
1 parent 668dd17 commit b480011

2 files changed

Lines changed: 37 additions & 0 deletions

File tree

content/docs/data-modeling/formulas.mdx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,37 @@ Keep them pure, dependency-free, and AI-readable.
196196

197197
---
198198

199+
## Build-time validation
200+
201+
`objectstack build` type-checks every expression against the object schema, so
202+
a mistake that would silently evaluate to `null` at runtime is caught before it
203+
ships. The same shared validator also runs when a flow is registered
204+
(`registerFlow`), so a flow authored dynamically gets the same verdict. Findings
205+
come at two severities: **errors** fail the build; **warnings** are advisory
206+
(surfaced, not fatal — promote them with `--strict`).
207+
208+
| Check | Example | Severity |
209+
|:---|:---|:---|
210+
| **Qualify field references** — a formula / validation / predicate binds only the `record` namespace, so a bare `amount` resolves to nothing and evaluates to `null`. Write `record.amount`. | `amount > 100``record.amount > 100` | error |
211+
| **Field must exist** — a typo'd `record.<field>` is flagged with a did-you-mean. | `record.amont``record.amount` | error |
212+
| **Unknown function** — a misspelled or nonexistent stdlib call. | `isBlnk(record.x)` | error |
213+
| **Type soundness** — a text or boolean field used with an arithmetic (`+ - * / %`) or ordering (`< > <= >=`) operator against a number faults at runtime and evaluates to `null`. Store it as a number field, or drop the arithmetic. | `record.title * 2` | warning |
214+
215+
> **Integer literals in arithmetic are fine.** `record.amount / 100`,
216+
> `record.price * 2`, `record.total - fee` all work — the engine handles mixed
217+
> `double × int` arithmetic, so you never need the `100.0` / `2.0` float-literal
218+
> workaround. Equality against any type is also safe (`record.stage == 5` simply
219+
> returns `false`), so only arithmetic/ordering on a genuinely text/boolean
220+
> field is flagged.
221+
222+
In **flow and automation conditions** the record's fields are bound *bare*
223+
(`stage == "won"`, not `record.stage`), so a bare reference is correct there.
224+
Instead, a bare identifier that is a near-miss of a real field gets a
225+
did-you-mean **warning** (a genuine flow variable is left alone), and the same
226+
type-soundness check applies.
227+
228+
---
229+
199230
## Cookbook
200231

201232
### Computed full name (handle nullable parts)

content/docs/data-modeling/validation.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@ functions available include:
337337
To compare against a field's previous value, reference `previous.<field>`
338338
(e.g. `record.stage != previous.stage`).
339339

340+
> **Qualify your references.** A validation predicate binds only the `record`
341+
> namespace, so a bare `status == "won"` resolves to nothing and the rule
342+
> silently never fires — write `record.status == "won"`. `objectstack build`
343+
> flags bare references, unknown fields, and text/boolean fields misused in
344+
> arithmetic — see [Build-time validation](/docs/data-modeling/formulas#build-time-validation).
345+
340346
## Best Practices
341347

342348
**DO:**

0 commit comments

Comments
 (0)