Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

Commit 5cabe16

Browse files
docs(core): state the ruled reference-day rule on FormulaFunctions TODAY() (objectui#10903) (#10915)
Fixes #10903 Clause-②: no ## What this does The `@object-ui/core` `FormulaFunctions` registration of `TODAY()` gains a docblock that states the rule ruled on the card (ruling comment 5864664470, option A). A client-evaluated `TODAY()` names the same reference day the server resolves for that user, on the compute-tz axis of objectstack ADR-0053 (Phase 2 items 5 and 6). While no reference timezone reaches the client, it names the UTC day. There is no behaviour change. The implementation reads no timezone, so it answers the UTC day, which is the ruled fallback. The one other file is an empty-frontmatter changeset, because `check-changeset-presence` asked for one: it counts any file under a released package's `src/`. ## The new docblock (on the `TODAY` registration inside `registerDateFunctions`) ```text `TODAY()` — the current calendar day, as a date-only `YYYY-MM-DD` string. Which day that is, is ruled (objectui#10903, ruling A): a client-evaluated `TODAY()` names the same reference day the server resolves for that user, on the compute-tz axis of objectstack ADR-0053 (`docs/adr/0053-date-and-datetime-semantics.md`, Phase 2 items 5 and 6), and the UTC day while no reference timezone reaches the client. The aim is one "today" per execution context: a client preview and the server's `today()` naming the same day. This implementation reads no timezone, so it answers the UTC day (`toISOString()`), which is the ruled fallback. It is deliberately not the viewer's local day. Until the reference timezone reaches the client (a later card), a server that resolves a reference timezone other than UTC can name a different day for part of every day. Once the timezone reaches the client, `TODAY()` names that timezone's calendar day instead. ``` No date and no mechanism is promised. The docblock cites the ADR by path and section, not by line. ## Other sentences touched: none No existing comment or doc says anything about `TODAY()`'s zone that the rule makes false, so no other sentence is edited. What was searched, case-sensitive `TODAY` over the whole tree outside changelogs: - `FormulaFunctions.ts`: the module docblock and the class docblock say nothing about zones, and `NOW()` and `DATEADD` have no docblock at all. - `ExpressionEvaluator.ts` and `predicateInput.ts`: their `today()` mentions are the lowercase CEL builtin on the `@objectstack/formula` path. They say where that builtin exists, not which day it names. - The flow simulator comment and its two i18n strings describe the server runtime's `NOW()` / `TODAY()` tokens, not core's. - `skills/objectui/guides/schema-expressions.md` lists `TODAY()` as "current date". That is not false under the rule, and `skills/**` is a governed surface, so it is left alone (see Acceptance notes). ## Proof that no code changed - **Comment-stripped emit.** `ts.transpileModule` (TypeScript 6.0.3, `removeComments: true`, ES2022 / ESNext) over `FormulaFunctions.ts` at base `733fd5ac6` and at head gives the same bytes. Both are sha256 `83e35ae792e548c790edc050fe5a35a51f4a9dc78c5227ff5fe9b84a7fc9b769`, 10,703 bytes, and `cmp` exits 0. - **Control.** The same instrument on a scratch copy with one same-length code change (the `TODAY` split index `[0]` changed to `[1]`) gives sha256 `d83ea446…` at the same 10,703 bytes, and `cmp` exits 1. So the check reads content, not length. - **Built output.** After `tsc`, `dist/evaluator/FormulaFunctions.d.ts` has 0 hits for "reference day" and declares only `private registerDateFunctions;`. The emitted `.js` has 1 hit, which is the positive control. The docblock cannot reach a published type. ## Verification (head `2e09f293e`) - Closure build `pnpm --workspace-concurrency=2 --filter '@object-ui/core...' run build` (types, then core, with `check-dist-completeness`): VERDICT command-exit 0. - `pnpm --filter @object-ui/core run type-check` echoes `tsc --noEmit && tsc -p tsconfig.test.json`. Then `pnpm exec vitest run packages/core/src/evaluator/ --maxWorkers=2` gives `Test Files 16 passed (16)` and `Tests 425 passed (425)`, VERDICT command-exit 0. - `scripts/__tests__/`, all 179 files, run in two halves under the verify lock. The first half gives `Test Files 90 passed (90)`, `Tests 3534 passed (3534)`. The second gives `Test Files 87 passed | 2 skipped (89)`, `Tests 1788 passed | 2 skipped (1790)`. Both are VERDICT command-exit 0. - These gates exit 0: `check-changeset-presence` (1 source file of 1 released package changed, 1 changeset declared, empty frontmatter), `changeset:check` (fixed group, no major), `check-changeset-overwrite`, `check:pending-changeset-literals`, `check:changeset-claims`, `check:control-bytes`, `check:new-line-citations` (`VERDICT new-cross-file-line-citations: 0 new citation(s)`), and `check-governed-queue-guard --test` over both paths (NOT GOVERNED). - Narrowed ESLint, root config with `--no-inline-config --format json`, over the one changed code file (1 result): 0 errors and 26 warnings at head, the same 0 and 26 on the base blob via `--stdin`. The resolved config has no `parserOptions.project` or `projectService`, so linting is not type-aware and a comment cannot move any untouched file's verdict. The repo-wide lint belongs to CI. - NOT MEASURED: `check:doc-examples`, because its prerequisite is that every package is built. Per its header, it compiles fenced `@example` blocks on exported declarations only, and this docblock has no `@example` and sits on a statement inside a private method. ## Acceptance notes - **Not this card's.** The ADR-0053 addendum is objectstack-ai/objectstack#20394 (Tier H), the client-timezone thread is filed when Phase 2 exposes the timezone, and the `DATEADD` / `DATEDIFF` / `DATEFORMAT` D1 slice rides objectui#10866. - **The interim split can already happen.** objectstack's `resolveExecutionContext` already resolves a tenant `localization.timezone` for server evaluation. Its tests pin the org-level setting and ignore per-user preference rows, which is organization-level only by design there. So the difference the docblock names is reachable today for a tenant with a non-UTC zone. The follow-up closes that gap. The docblock names no precedence for this reason: ADR-0053 Phase 2 item 5 still says "optionally overridden by `sys-user-preference.timezone`". That ADR paragraph is the one objectstack-ai/objectstack#20394 edits, so the drift is noted there for its author, not filed. - **The CEL `today()` gets no timezone from core either.** It is reached via `evaluateCondition` with a `cel` envelope, into `@objectstack/formula`. There are zero `timezone` hits under `packages/core/src/evaluator/`, so it also answers the UTC day, which is consistent with the ruled fallback. The ruling names `FormulaFunctions` `TODAY()`, so that path is untouched. - **The skills guide line.** `skills/objectui/guides/schema-expressions.md` "current date" is left as is, because it is governed and not falsified. If the seat wants the published guide to carry the rule, that needs its own governed PR. --- _Generated by [Claude Code](https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 492e79e commit 5cabe16

2 files changed

Lines changed: 24 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
---
3+
4+
Comment-only in `@object-ui/core`: the built-in `TODAY()` formula function now carries a docblock stating which calendar day it names, as ruled on objectui#10903 (option A). A client-evaluated `TODAY()` names the same reference day the server resolves for that user, on the compute-tz axis of objectstack ADR-0053, and the UTC day while no reference timezone reaches the client. The implementation reads no timezone, so it answers the UTC day, which is the ruled fallback; nothing it returns changes.
5+
6+
Declared as releasing nothing because the emit was measured rather than assumed: the docblock sits inside the private `registerDateFunctions` body, so it cannot reach the emitted `FormulaFunctions.d.ts`, and the comment-stripped emit of `FormulaFunctions.ts` is byte-identical to the base. No published behaviour changes (objectui#10903).

‎packages/core/src/evaluator/FormulaFunctions.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,24 @@ export class FormulaFunctions {
122122
// ==========================================================================
123123

124124
private registerDateFunctions(): void {
125+
/**
126+
* `TODAY()` — the current calendar day, as a date-only `YYYY-MM-DD` string.
127+
*
128+
* Which day that is, is ruled (objectui#10903, ruling A): a client-evaluated
129+
* `TODAY()` names the same reference day the server resolves for that
130+
* user, on the compute-tz axis of objectstack ADR-0053
131+
* (`docs/adr/0053-date-and-datetime-semantics.md`, Phase 2 items 5 and 6),
132+
* and the UTC day while no reference timezone reaches the client. The aim
133+
* is one "today" per execution context: a client preview and the server's
134+
* `today()` naming the same day.
135+
*
136+
* This implementation reads no timezone, so it answers the UTC day
137+
* (`toISOString()`), which is the ruled fallback. It is deliberately not
138+
* the viewer's local day. Until the reference timezone reaches the client
139+
* (a later card), a server that resolves a reference timezone other than
140+
* UTC can name a different day for part of every day. Once the timezone
141+
* reaches the client, `TODAY()` names that timezone's calendar day instead.
142+
*/
125143
this.register('TODAY', (): string => {
126144
const now = new Date();
127145
return now.toISOString().split('T')[0];

0 commit comments

Comments
 (0)