Skip to content

fix(spec,service-analytics): the matrix's token axis + its sixth consumer, and the dropped $between it found (ADR-0053 D-A3.1) - #4109

Merged
os-zhuang merged 6 commits into
mainfrom
claude/adr-0053-temperature-consistency-akyja5
Jul 30, 2026
Merged

fix(spec,service-analytics): the matrix's token axis + its sixth consumer, and the dropped $between it found (ADR-0053 D-A3.1)#4109
os-zhuang merged 6 commits into
mainfrom
claude/adr-0053-temperature-consistency-akyja5

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

#4081(ADR-0053 D-A3)的补完。矩阵本体已由 #4098 先行落地;本 PR 与其合流,补上 #4098 的 ADR 附录里明确记为仍然开放的两处覆盖,并修掉补覆盖时矩阵当场抓到的缺陷——以及顺着成因挖出的一整类。

一、relative-token 轴(#4098 附录记为开放)

四次事故都发生在 token 解析之下,但作者实际书写的就是 {today} / {90_days_ago} / {current_month_end},而没有任何测试证明解析后的比较值在各后端落到同一批行。

  • TemporalCase 新增 tokenFilter?dateRange?,加上钉死的参考瞬间 TEMPORAL_NOW
  • 能触达 @objectstack/core 的消费者把 token 拼写解析后必须落在与字面量拼写相同的行集——resolver 漂移与 evaluator 漂移由此可分。formula 依赖刻意止于 spec,只跑字面量。
  • 同时补:TemporalRow.writerForm(D-E4 混合写入者播种)、负 epoch 行、date 的相等与 $in[P2] Provide a declarative time-relative trigger (avoid fragile date-equality on record-change) #1874 起源形态)、driver-sqlite-wasm 继承护栏、driver-sql 未回填 legacy 双扫。

二、第六个消费者:NativeSQLStrategy

它正是 #3650 事发的那个面,也是唯一手工拼 SQL、不走驱动编译器的后端,却在矩阵的后端表里没有消费者。原因很说明问题:它现有的套件断言的都是 SQL 字符串,而谓词被丢掉对字符串断言完全隐形——SQL 依然合法,只是更宽。新消费者用真实引擎(sql.js)执行并断言行 id

三、它当场抓到的缺陷:$between 谓词凭空消失

首跑即红:$between 返回全表。成因在共享的 filter-normalizer——MONGO_TO_CUBE_OP 里没有 $between,未映射的算子被 continue 掉,谓词直接从 WHERE 消失。两条策略都吃这个归一化器,所以都中招。用户侧症状就是 #3650:带区间过滤的图表画出整个数据集。

修法:$between 下降为它的两个界gte + lte),上界自动继承各策略已有的整日日历规则,不需要第二份实现——与 #4098 修 preview 的做法一致。

四、收口成因本身(Closes #4128

只修 $between 等于只修实例。按同一把尺子把整个可授权词汇过了一遍,又挖出两处比「丢弃」更糟的——语义被悄悄写反

算子 修复前 修复后
$startsWith / $endsWith 静默丢弃 → 全表 带锚点的 LIKE 'x%' / LIKE '%x';ObjectQL 侧透传规范算子,锚点不依赖 regex 方言
$null 静默丢弃 → 全表。这是 Console「为空/不为空」筛选器产生的形状,即这类 widget 一直在显示全部行 按布尔值编译为 IS NULL / IS NOT NULL
$exists 与取值无关地映射成 set{$exists: false} 编译成 IS NOT NULL——与语义完全相反 $null 一起显式解析:键→名的映射表表达不了「含义随取值翻转」的算子,这正是反转的成因
$notContains ObjectQLStrategy 没有 case,落到 default: return v0 返回裸值——「不包含 x」被编译成「等于 x 透传 $notContains
未知算子 归一化层 continue 丢弃;ObjectQL 层当成相等 两处都抛错#3948 先例)

$or / $not 仍跳过(需要递归 WHERE 构建器),但这一条现在写进模块文档作为已声明的缺口,不再是隐性的。

顺带更正我在 #4128 里的一处错误:原文把 $regex 列为 spec 词汇,实际是我 grep 时把注释当成了声明——可授权词汇里没有它。

验证(本地全绿)

结果
spec 6987
service-analytics 413(原 360)
driver-sql 矩阵 43(16 字面 + 11 token + 16 legacy)
driver-sqlite-wasm / driver-memory / formula 27 / 27 / 16

两个新套件都用 git stash 实测过修复前的行为:$between 4 例红、算子覆盖 6 例红。mongodb 套件本沙箱无二进制而 skip,CI 真实执行。spec 八个生成物 gate 全 PASS。

Refs #4081Closes #4128

🤖 Generated with Claude Code

https://claude.ai/code/session_01TqqZmPS5a4gJGBoCTwipFr

…atetime backends (ADR-0053 D-A3) (#4081)

The temporal seam broke four times (#3650 window dropped entirely, #3773
epoch-as-Julian-day NULL buckets, #3777 bare-day upper bound losing the
final day, #4047 type-bracket comparison returning nothing on memory/mongo),
and each break was found by accident: every fix left a suite proving its own
issue against its own fixture, with no shared standard to hold the six
evaluation surfaces together.

This adds the standard — the temporal twin of filter-logic-conformance
(#3774), and the one ADR-0053 decision (D-A3) that had never been built:

- `packages/spec/src/data/temporal-conformance.ts` exports
  `TEMPORAL_CONFORMANCE_ROWS` (one fixture spanning exact-midnight,
  intra-day, next-midnight, month-end last-millisecond, leap-day and
  pre-epoch instants, each tagged with a writer form for the D-E4
  mixed-writer axis) and `TEMPORAL_CONFORMANCE_CASES`
  ({ name, fieldType, operator, filter, tokenFilter?, dateRange?, expected,
  note }) — field-type × operator × bound-semantics (D-D2) × relative-token
  cells, asserting row-id sets, never emitted SQL. Each note names the
  incident the case guards.
- Six thin consumers: driver-sql (canonical + un-backfilled legacy storage;
  live PG/MySQL via the existing CI temporal job), driver-sqlite-wasm
  (inheritance guard), driver-memory and driver-mongodb (mixed-writer
  seeds), formula's matchesFilterCondition (RLS write-side check),
  and the analytics preview evaluator (where + timeDimensions.dateRange,
  the #3650 surface).
- The token axis runs end-to-end: consumers that can reach
  @objectstack/core resolve each case's tokenFilter/dateRange against the
  pinned TEMPORAL_CONFORMANCE_NOW and must land on the same rows as the
  literal spelling, so resolver drift and evaluator drift are
  distinguishable at a glance.

Deliberate scope, same discipline as the filter-logic table: a case belongs
here only if every backend must agree on it. The schema-aware-only cells
(bare-day $eq/$in on datetime, $gt at an exact stored midnight, Field.time)
stay pinned in the per-driver suites and are documented as exclusions.

Closes #4081.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqqZmPS5a4gJGBoCTwipFr
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 30, 2026 11:12am

Request Review

…n top

Main landed its own D-A3 matrix (#4098: TEMPORAL_ROWS/TEMPORAL_CASES + the
preview $between fix) while this branch was in flight. Resolution keeps
main's landed table, naming and consumers as the base, and re-applies this
branch's increments — exactly the coverage #4098's ADR addendum recorded as
still open:

- the relative-token axis: TemporalCase.tokenFilter/dateRange + TEMPORAL_NOW,
  resolved via @objectstack/core resolveFilterTokens in the five
  core-reachable consumers (formula stays literal-only by dependency stance)
- TemporalRow.writerForm: mixed writer-population seeding (D-E4) in the
  sql/wasm/memory/mongo consumers
- a pre-epoch fixture row, and the date equality/$in cells (#1874's shape)
- a driver-sqlite-wasm consumer pinning the inherited SqlDriver seam
- a legacy-storage sweep in the driver-sql consumer (un-backfilled mixed
  epoch/naive column through the read-repair path)

The duplicate preview consumer from this branch is dropped in favour of
main's preview-temporal-conformance.test.ts, extended with the token and
timeDimensions.dateRange sweeps; this branch's changeset is rewritten as the
increment (.changeset/temporal-conformance-token-axis.md) alongside #4098's.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqqZmPS5a4gJGBoCTwipFr
@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests size/m tooling labels Jul 30, 2026
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): packages/services, @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via packages/services, @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via packages/services, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services, @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

@os-zhuang os-zhuang changed the title test(spec): temporal conformance matrix — one standard for six date/datetime backends (ADR-0053 D-A3) (#4081) test(spec): the temporal conformance matrix gains its relative-token axis (ADR-0053 D-A3, #4081 follow-up) Jul 30, 2026
…$between it found (ADR-0053 D-A3.1)

NativeSQLStrategy is the surface #3650 was actually about, and it was listed
in the conformance matrix's own backend table with no consumer — because
every existing suite for it asserts the emitted SQL string, and a DROPPED
predicate is invisible to a string assertion: the SQL stays valid, just
wider. Executing the shared cases against a real engine (sql.js, the same
pure-WASM engine driver-sql falls back to) and asserting row ids is what
D-A3 asked for, and it immediately showed `$between` returning the entire
table.

The cause sat one layer below the strategy, in the shared filter-normalizer:
`$between` was absent from MONGO_TO_CUBE_OP, and an unmapped operator is
`continue`d, so the predicate vanished from the compiled WHERE clause. Both
strategies read that normalizer, so the ObjectQL aggregate path was affected
too. User-visible symptom: a widget with a range filter charts the whole
dataset, with nothing in the SQL to suggest a filter was ever requested.

`$between [min, max]` now LOWERS to its two bounds (gte + lte) rather than
gaining an operator of its own, so a range's max inherits the calendar-day
whole-day rule (#3777) from each strategy's existing upper-bound handling —
NativeSQLStrategy compiles a bare-day upper bound half-open itself, the
ObjectQL path gets the same rule from the driver — instead of needing a
second implementation to keep in step. That is how #4098 closed the same
defect on the preview evaluator. A malformed `$between` throws rather than
being dropped, the stance driver-memory took for the same shape in #3948.

The consumer runs the literal, relative-token and timeDimensions.dateRange
spellings of every shared case. Rows are seeded canonical, where the
driver's temporal hooks are identities, so the context omits them and
exercises the same "absent = identity" path Postgres takes (D-A2); the
un-backfilled mixed column stays covered where the driver truth lives.

The cause itself is NOT fully closed: `$startsWith`, `$endsWith`, `$null`
and `$regex` are still silently dropped by the same `continue`. Filed as
#4128 rather than expanded into this change, with the #3948 precedent for
turning the fallback into a throw.

service-analytics 391 green (was 360; +31 from the new consumer).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqqZmPS5a4gJGBoCTwipFr
@github-actions github-actions Bot added size/l and removed size/m labels Jul 30, 2026
@os-zhuang os-zhuang changed the title test(spec): the temporal conformance matrix gains its relative-token axis (ADR-0053 D-A3, #4081 follow-up) fix(spec,service-analytics): the matrix's token axis + its sixth consumer, and the dropped $between it found (ADR-0053 D-A3.1) Jul 30, 2026
claude added 2 commits July 30, 2026 10:44
The module doc described the shape it produces but never what it actually
maps — and in a normalizer, that omission is load-bearing: an unmapped
operator is `continue`d, which does not "not support" the predicate, it
DELETES it, widening the query to rows the author excluded. That is how the
$between defect survived (see #4128 for the four operators still in that
state, and the #3948 precedent for making the fallback throw).

Mirrors the scope comment the preview evaluator carries for the same reason.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqqZmPS5a4gJGBoCTwipFr
…query (#4128)

Closes the CAUSE behind the $between defect, not just that instance.
`normalizeAnalyticsFilters` skipped any operator missing from its map, and a
skipped predicate does not narrow a query — it widens it: the SQL stays valid
and returns rows the author excluded. Four operators from the spec's
authorable vocabulary sat in that state, and a fifth was mapped wrongly:

- $startsWith / $endsWith were dropped. Both strategies now compile them —
  anchored LIKE on the raw-SQL path, the canonical operators (which every
  driver implements directly) on the ObjectQL path, so an anchored match does
  not depend on regex dialect.
- $null was dropped. It is what the console emits for an "is empty" filter,
  so such a widget showed every row.
- $exists was mapped value-INDEPENDENTLY to `set`, so {$exists: false}
  compiled to IS NOT NULL — the inverse of what it asks. It and $null are now
  resolved explicitly: a key→name map cannot express an operator whose meaning
  flips with its value, which is exactly how that inversion got in.
- $notContains reached ObjectQLStrategy, which had no arm for it and fell to a
  `default` returning a bare value — compiling "does not contain x" as
  "equals x".
- Unknown operators now THROW on both surfaces rather than being dropped
  (normalizer) or reinterpreted as an equality (ObjectQL). #3948's call for
  the same shape.

$or / $not remain skipped — expressing them needs a recursive WHERE builder
rather than the flat array the strategies consume. That gap is declared in the
module doc rather than silent.

filter-operator-coverage.test.ts runs the whole vocabulary against a real
SQLite engine and asserts ROW IDS; six of its cases fail without this change.
A dropped predicate is invisible to the SQL-string assertions the strategies'
other suites use, which is how these survived. service-analytics 413 green.

Closes #4128.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqqZmPS5a4gJGBoCTwipFr

Copy link
Copy Markdown
Contributor Author

CI 说明:Lint & Type Check 的红不是本 PR 引入的,在 base 分支上同样失败。

失败的其实不是 ESLint 步骤,而是同一 job 里的 check:wildcard-fallthrough#4116 新加的守卫),报的是 packages/adapters/hono/src/index.ts 的一个挂载——本 PR 未触及该文件,且本分支根本不含这个守卫脚本(CI 在 pull_request 下跑的是 PR 与 base 的合并树)。

在纯净的 origin/main4f30943,无任何本 PR 改动)上复现,错误逐字相同:

✗ wildcard fall-through guard (#4116)
  packages/adapters/hono/src/index.ts:all `${prefix}/storage/*`
    DECLARED but not found by the scan. Moved, renamed or deleted? Update MOUNTS.

成因是两个 PR 的竞态:#4112 删掉了那个挂载,#4116 在守卫账本里声明了它,先后合入未互相 rebase。已单独立项 #4140(含复现步骤与建议的销账式修法)。

本 PR 的其余检查照常,待 main 恢复后我会重跑并推进合并。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review July 30, 2026 11:26
@os-zhuang
os-zhuang merged commit 7a55913 into main Jul 30, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/adr-0053-temperature-consistency-akyja5 branch July 30, 2026 11:26
os-zhuang pushed a commit that referenced this pull request Jul 30, 2026
The temporal conformance matrix (#4109) gives every case a `tokenFilter`
spelling, and the driver + analytics suites resolve it before asserting.
This suite runs only the literal `filter`, with nothing saying why — so
the absence reads as an omission, and the next person either "fixes" a
non-gap or assumes coverage that cannot exist.

It is architecture: an RLS `check` is a CEL expression (`check` is a
string, compiled by rlsCompiler.compileFilter) where a relative date is
the function today() evaluated at compile time. A {token} string never
reaches this evaluator — resolveFilterTokens runs on the ObjectQL read
path and the write-side check does not go through it; verified that
plugin-security never calls the resolver.

Comment only; the suite is unchanged and still 13/13.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EkL3RGJrzjLEGURsLxfS2f
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 protocol:data size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

analytics filter-normalizer:未映射的算子被静默丢弃 → 查询放宽到全表($between 已修,还剩四个)

2 participants