This repository was archived by the owner on Oct 26, 2026. It is now read-only.
Repository navigation
Commit 5cabe16
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
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
125 | 143 | | |
126 | 144 | | |
127 | 145 | | |
| |||
0 commit comments