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

vercelBot commented Jun 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 15, 2026 6:32pm

Request Review

@github-actionsgithub-actionsBot 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 mainJun 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-zhuang