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

fix(objectql): thread execution context into read-time formula evaluation#1988
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 }:

constr=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 nowpinned 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:

constnow=nowSnapshot??newDate();constuser=execCtx?.userId ? {id: String(execCtx.userId),role: execCtx?.roles?.[0]} : undefined;constorg=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

vercelBot commented Jun 16, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 16, 2026 10:25pm

Request Review

@github-actionsgithub-actionsBot 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.

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.

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

1 participant

@os-zhuang