test(formula): lock ADR-0053 Phase 2 Slice 3 acceptance criteria (#1980)#3181
Merged
Merged
Conversation
Add acceptance tests for the timezone-aware today()/daysFromNow()/daysAgo() functions (compute-tz core of ADR-0053 Phase 2, decision D1). The implementation already shipped (#1998/#2001/#2006); these lock the issue's criteria and pin the DST-boundary + equality behavior: - AC1: today() at 2026-06-16T02:00Z in America/Los_Angeles == UTC-midnight of 2026-06-15. - AC3: reference tz unset vs 'UTC' is byte-for-byte the pre-Phase-2 behavior for all three functions. - AC2: calendar days are correct across both 2026 US DST transitions (spring-forward Mar 8, fall-back Nov 1); a Field.datetime instant compares equal to daysFromNow(n) across DST; a Field.date string matches via the hydration-safe idioms (ordering operators, date(), daysBetween()). - A characterization guard documents the known cel-js equality limitation: a bare `date-string == today()` silently returns false because cel-js's isEqual hard-codes `string == X` to false. This is timezone-independent and cross-cutting; the fix belongs in the data layer (hydrate date fields to Date where field types are known) and is tracked as a separate follow-up. Test-only; no changeset (no functional change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SuiM565BZ3TR1VD3prMguB
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 18, 2026 06:51
This was referenced Jul 18, 2026
os-zhuang
pushed a commit
that referenced
this pull request
Jul 18, 2026
…arison rewrite (#3183) A `Field.date` reads back as a "YYYY-MM-DD" string (ADR-0053 Phase 1), and cel-js's equality treats a string and a timestamp as unequal without consulting any overload, so `record.due_date == today()` silently returned false (and `!= today()` silently true) even for a same-day record. celEngine.evaluate now rewrites the parsed AST: for each `==`/`!=` whose one operand is today()/daysFromNow()/daysAgo()/now(), the field operand is wrapped in date(...) (the stdlib coercion), then serialized and evaluated — so `record.due_date == today()` runs as `date(record.due_date) == today()`. - Per-occurrence: a mixed `d == "literal" || d == today()` keeps the literal comparison intact while fixing the temporal one. - Type-blind-safe: date() degrades gracefully (already-Date passes through; non-date string / null → Invalid Date → stays false), so no field types are needed and no currently-correct result is worsened. - Cheap: reserializes only when such a comparison is present (plain-includes gate) and memoizes source → rewritten source. AST-based, no regex on input. Covers every interpreter site (formulas, defaults, validation, hooks, flow conditions) via the single evaluate chokepoint. RLS/sharing unaffected (cel-to-filter rejects function calls loudly). Supersedes the #3192 advisory lint (checkTemporalDateEquality + the temporalEqualityFields helper), now removed — with the runtime fixed it would be a false alarm. Flips the #3181 KNOWN GAP characterization test to assert the fix, and adds an objectql end-to-end test (a date formula field read via find()). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SuiM565BZ3TR1VD3prMguB
os-zhuang
added a commit
that referenced
this pull request
Jul 18, 2026
…arison rewrite (#3183) (#3205) A `Field.date` reads back as a "YYYY-MM-DD" string (ADR-0053 Phase 1), and cel-js's equality treats a string and a timestamp as unequal without consulting any overload, so `record.due_date == today()` silently returned false (and `!= today()` silently true) even for a same-day record. celEngine.evaluate now rewrites the parsed AST: for each `==`/`!=` whose one operand is today()/daysFromNow()/daysAgo()/now(), the field operand is wrapped in date(...) (the stdlib coercion), then serialized and evaluated — so `record.due_date == today()` runs as `date(record.due_date) == today()`. - Per-occurrence: a mixed `d == "literal" || d == today()` keeps the literal comparison intact while fixing the temporal one. - Type-blind-safe: date() degrades gracefully (already-Date passes through; non-date string / null → Invalid Date → stays false), so no field types are needed and no currently-correct result is worsened. - Cheap: reserializes only when such a comparison is present (plain-includes gate) and memoizes source → rewritten source. AST-based, no regex on input. Covers every interpreter site (formulas, defaults, validation, hooks, flow conditions) via the single evaluate chokepoint. RLS/sharing unaffected (cel-to-filter rejects function calls loudly). Supersedes the #3192 advisory lint (checkTemporalDateEquality + the temporalEqualityFields helper), now removed — with the runtime fixed it would be a false alarm. Flips the #3181 KNOWN GAP characterization test to assert the fix, and adds an objectql end-to-end test (a date formula field read via find()). Claude-Session: https://claude.ai/code/session_01SuiM565BZ3TR1VD3prMguB Co-authored-by: Claude <noreply@anthropic.com>
12 tasks
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Jul 20, 2026
…arison rewrite (objectstack-ai#3183) A `Field.date` reads back as a "YYYY-MM-DD" string (ADR-0053 Phase 1), and cel-js's equality treats a string and a timestamp as unequal without consulting any overload, so `record.due_date == today()` silently returned false (and `!= today()` silently true) even for a same-day record. celEngine.evaluate now rewrites the parsed AST: for each `==`/`!=` whose one operand is today()/daysFromNow()/daysAgo()/now(), the field operand is wrapped in date(...) (the stdlib coercion), then serialized and evaluated — so `record.due_date == today()` runs as `date(record.due_date) == today()`. - Per-occurrence: a mixed `d == "literal" || d == today()` keeps the literal comparison intact while fixing the temporal one. - Type-blind-safe: date() degrades gracefully (already-Date passes through; non-date string / null → Invalid Date → stays false), so no field types are needed and no currently-correct result is worsened. - Cheap: reserializes only when such a comparison is present (plain-includes gate) and memoizes source → rewritten source. AST-based, no regex on input. Covers every interpreter site (formulas, defaults, validation, hooks, flow conditions) via the single evaluate chokepoint. RLS/sharing unaffected (cel-to-filter rejects function calls loudly). Supersedes the objectstack-ai#3192 advisory lint (checkTemporalDateEquality + the temporalEqualityFields helper), now removed — with the runtime fixed it would be a false alarm. Flips the objectstack-ai#3181 KNOWN GAP characterization test to assert the fix, and adds an objectql end-to-end test (a date formula field read via find()). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SuiM565BZ3TR1VD3prMguB
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 of #1980 (ADR-0053 Phase 2 · Slice 3 — compute-tz core). Design: #1975 · Parent: #1928.
The implementation shipped across #1998 / #2001 / #2006. This PR adds the acceptance tests that lock the issue's criteria and pin the DST-boundary + equality behavior D1 promises. Test-only — no functional change, no changeset.
What the tests cover
All in
packages/formula/src/cel-engine.test.ts, in a newADR-0053 Phase 2 · Slice 3 acceptance criteria (#1980)block:today()at2026-06-16T02:00Zwith reference tzAmerica/Los_Angeles→ the UTC-midnightDateof2026-06-15(plusdaysFromNow(1)/daysAgo(1)).'UTC'is byte-for-byte identical for all three functions (today's behavior preserved).Field.datetimeinstant compares equal todaysFromNow(n)across DST; aField.datestring matchestoday()/daysAgo()via the hydration-safe idioms (ordering operators,date(...),daysBetween(...) == 0).Verification
@objectstack/formula— 213 tests pass (7 new),tsc --noEmitclean.AC2 lists
record.due_date == today(). AField.datereads back as aYYYY-MM-DDstring (ADR-0053 Phase 1, #1968), and cel-js's equality hard-codesstring == Xtofalse(overloads.jsisEqualreturns false for a string left operand without ever consulting a registered overload — and refuses cross-type object equality). So a baredate-string == today()silently returnsfalse:==/!=: formulas, validation, RLS, flow conditions, action predicates) — so it is out of scope for this slice and cannot be fixed in the formula operator layer.Datebefore evaluation where field-type metadata is known.date(record.d) == today(), ordering operators, ordaysBetween(today(), record.d) == 0.A characterization guard test locks this known behavior (clearly marked
KNOWN GAP, not an endorsement) so it can't silently change. Recommend tracking the fix as its own issue.🤖 Generated with Claude Code
Generated by Claude Code