Skip to content

fix(objectql): thread execution context into read-time formula evaluation - #1988

Merged
os-zhuang merged 1 commit into
mainfrom
fix/applyformulaplan-context-1979
Jun 16, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
fix/applyformulaplan-context-1979

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #1979.

What

applyFormulaPlan — which computes Field.formula virtual fields after find/findOne — evaluated each expression with only { record }:

const r = ExpressionEngine.evaluate(fp.expression, { record: rec });

So:

  • a formula using now()/today() ran against a fresh wall-clock read on every evaluation — no determinism within one read;
  • a formula referencing the caller (os.user.id, os.org.id) faulted and fell back to null, because user/org were never in scope.

Fix

Build the eval context exactly the way applyFieldDefaults already does — a now pinned once per operation (every row and every formula field in one read observes the same instant) plus os.user / os.org resolved from the ExecutionContext:

const now = nowSnapshot ?? new Date();
const user = execCtx?.userId ? { id: String(execCtx.userId), role: execCtx?.roles?.[0] } : undefined;
const org  = execCtx?.tenantId ? { id: String(execCtx.tenantId) } : undefined;
// …evaluate(fp.expression, { now, user, org, record: rec })

The two call sites in find()/findOne() pass opCtx.context (already in scope). Read-time formulas now behave consistently with default-value expressions.

Scope

This is the read-path slice of ADR-0053 Phase 2 (#1975), but it is independent of timezone and valuable on its own (determinism + caller-aware computed fields). #1980 will additionally thread timezone here once ExecutionContext.timezone exists.

Testing

  • New: a read-time formula resolves os.user.id / os.org.id from the execution context; now() is pinned identically across all rows in one find.
  • Full @objectstack/objectql suite green — 639 tests; DTS build + typecheck clean.
  • Changeset: patch (lockstep fixed group).

🤖 Generated with Claude Code

…tion (#1979)

`applyFormulaPlan` computes Field.formula virtual fields after find/findOne,
but evaluated each expression with only `{ record }`. So a formula using
now()/today() ran against a fresh wall-clock read on every evaluation (no
determinism), and a formula referencing the caller (os.user.id / os.org.id)
faulted and fell back to null because user/org were never in scope.

Build the eval context the same way applyFieldDefaults already does: a `now`
snapshot pinned once per operation (every row + every formula field in one
read observes the same instant) plus os.user / os.org resolved from the
ExecutionContext. The two call sites in find()/findOne() pass opCtx.context.

Independent of timezone; the read-path prerequisite for ADR-0053 Phase 2
(#1980 will additionally thread `timezone` here once ExecutionContext.timezone
exists).

Tests: read-time formula resolves os.user.id/os.org.id from context; `now()`
is pinned identically across all rows in one find. Full objectql suite green
(639). Closes #1979.

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

vercel Bot commented Jun 16, 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 16, 2026 10:25pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Jun 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql.

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

  • content/docs/concepts/core/services.mdx (via @objectstack/objectql)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/objectql)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/concepts/packages.mdx (via @objectstack/objectql)
  • content/docs/guides/authentication.mdx (via @objectstack/objectql)
  • content/docs/guides/deployment-vercel.mdx (via @objectstack/objectql)
  • content/docs/guides/formula.mdx (via packages/objectql)
  • content/docs/guides/kernel-services.mdx (via @objectstack/objectql)
  • content/docs/guides/objectql-migration.mdx (via @objectstack/objectql)
  • content/docs/guides/packages.mdx (via @objectstack/objectql)
  • content/docs/guides/plugins.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/v9.mdx (via @objectstack/objectql)

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.

This branch was successfully deployed

1 active deployment
Preview — 41393027 Deployed Jun 16, 2026 by vercel[bot]
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.

ADR-0053 Phase 2 · Slice 2: thread context into applyFormulaPlan (read-time formula fields)

1 participant