feat(validate): flag bare field refs in record-scoped CEL sites (#1928)#1931
Merged
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 18 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Jul 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 2 of 2 for #1928 (build-time check). Part 1 is the runtime fix #1930.
Problem
A
Field.formulaand an object validation predicate evaluate against therecordnamespace only — no field flattening — so a bare top-level identifier likeamountorstatusresolves to nothing and the expression silently evaluates tonull/ never fires. This is the silent-at-runtime class behind #1927's brokenexpected_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
validateExpressiongains an evaluationscope:scope: 'record'(field formulas, validation predicates) → a bare reference is reported with the corrective form: writerecord.<field>.scope: 'flattened'(flow / automation conditions, the default) → the record's fields ARE spread to top-level alongside flow variables, so barestatus/budgetare correct and are not flagged.The detector (
detectBareReference) runs cel-jscheck()in arecord-scoped environment (namespace roots declared, real stdlib registered) and acts only on theUnknown variablefault — so it cannot false-positive on arithmetic/comparison/null-guard type overloads (verified:record.lead_score != null && record.lead_score > 100passes).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_leadvalidationlead_score_range— barelead_score→ rule silently never firedfield_zoo.f_formula, showcaseproject.budget_remaining— bare refs → nullVerification
objectstack buildwith the corrective message.@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