Skip to content

feat(validate): flag bare field refs in record-scoped CEL sites (#1928)#1931

Merged
os-zhuang merged 1 commit into
mainfrom
feat/validate-formula-types
Jun 16, 2026
Merged

feat(validate): flag bare field refs in record-scoped CEL sites (#1928)#1931
os-zhuang merged 1 commit into
mainfrom
feat/validate-formula-types

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Part 2 of 2 for #1928 (build-time check). Part 1 is the runtime fix #1930.

Problem

A Field.formula and an object validation predicate evaluate against the record namespace only — no field flattening — so a bare top-level identifier like amount or status resolves to nothing and the expression silently evaluates to null / never fires. This is the silent-at-runtime class behind #1927's broken expected_revenue/full_name/is_closed, and exactly the mistake an AI author makes.

The build validator already walked every expression site but only checked CEL syntax + record.<field> existence — bare refs slipped through.

Fix

validateExpression gains an evaluation scope:

  • scope: 'record' (field formulas, validation predicates) → a bare reference is reported with the corrective form: write record.<field>.
  • scope: 'flattened' (flow / automation conditions, the default) → the record's fields ARE spread to top-level alongside flow variables, so bare status/budget are correct and are not flagged.

The detector (detectBareReference) runs cel-js check() in a record-scoped environment (namespace roots declared, real stdlib registered) and acts only on the Unknown variable fault — so it cannot false-positive on arithmetic/comparison/null-guard type overloads (verified: record.lead_score != null && record.lead_score > 100 passes).

Why not flag bare refs in flow conditions too?

Flow conditions flatten the record's fields and flow-defined variables (e.g. expiring_deals.length > 0, new_opportunity.stage) into one namespace. Flow variables aren't schema-knowable, so a bare identifier there can't be soundly told apart from a typo — flagging it would false-positive and break valid builds. The design law for this subsystem: the build check may only reject what the runtime would also fail. So flow-condition bare refs stay out of the hard gate (candidate for a future advisory warning — see #1928).

Also fixes 3 latent bugs the new check surfaced

  • crm_lead validation lead_score_range — bare lead_score → rule silently never fired
  • showcase field_zoo.f_formula, showcase project.budget_remaining — bare refs → null

Verification

  • Negative test: reintroducing a bare ref fails objectstack build with the corrective message.
  • All three example apps build clean (they contain many correct bare-ref flow conditions → zero false positives).
  • New unit tests: 7 in @objectstack/formula (validate.test.ts), 4 in @objectstack/cli (validate-expressions.test.ts). Full suites green (formula 101, cli 410).

Refs #1928, #1927. Sibling: #1930 (runtime overloads).

🤖 Generated with Claude Code

A Field.formula and an object validation predicate evaluate against the
`record` namespace only (no field flattening), so a bare top-level identifier
(`amount`, `status`) resolves to nothing and the expression silently evaluates
to null / never fires — the silent-at-runtime class behind #1927.

`validateExpression` gains an evaluation `scope`; for `scope: 'record'` it
reports a bare reference with the corrective `record.<field>` form. It acts only
on cel-js's `Unknown variable` fault, so it cannot false-positive on
arithmetic/comparison/null-guard overloads. Flow & automation conditions keep
the default `scope: 'flattened'` — the record's fields are spread to top-level
there alongside flow variables, so bare refs are correct and NOT flagged.
`objectstack compile` wires record scope for field formulas + validation
predicates; flow conditions stay flattened.

Also fixes three latent bare-ref bugs surfaced by the new check:
- crm_lead validation `lead_score_range` (rule silently never fired)
- showcase field_zoo `f_formula`, showcase project `budget_remaining` (null)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 15, 2026 6:32pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jun 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/formula.

18 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/cloud-artifact-api.mdx (via packages/cli)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/cli)
  • content/docs/concepts/packages.mdx (via @objectstack/cli, @objectstack/formula)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/cli)
  • content/docs/guides/authentication.mdx (via @objectstack/cli)
  • content/docs/guides/client-sdk.mdx (via @objectstack/cli)
  • content/docs/guides/formula.mdx (via @objectstack/formula)
  • content/docs/guides/hook-bodies.mdx (via packages/cli)
  • content/docs/guides/metadata/validation.mdx (via @objectstack/formula)
  • content/docs/guides/packages.mdx (via @objectstack/cli, @objectstack/formula)
  • content/docs/guides/project-scoping.mdx (via @objectstack/cli)
  • content/docs/guides/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/guides/runtime-services/index.mdx (via packages/cli)
  • content/docs/guides/skills.mdx (via packages/cli)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/formula)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang merged commit ff0a87a into main Jun 16, 2026
14 checks passed
@os-zhuang
os-zhuang deleted the feat/validate-formula-types branch June 16, 2026 02:35
os-zhuang added a commit that referenced this pull request Jun 16, 2026
…#1935)

The new build-time error (#1931) can fail a previously-passing build for a stack
carrying a latent bare-ref bug, so it warrants a minor bump and an explicit
release note rather than a silent patch.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant