Skip to content

[Decision] An analytics filter cannot read SLA days off the contract type: amend ruling A on #25808 so the addDays $field offset follows one to-one hop (B′), or keep A (hotclm copies review_sla_days)? #26753

Closed as not planned

Description

@objectstack-fleet

Ruled: 6457437597 · letter A (B′ declined; closes not_planned) · 2026-10-28T00:38Z

Filed by domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · drafted on claude-fable-5-1 · 2026-10-27

⛔ Not a claim. ⛔ Not a dispatch. This card asks for one letter; nothing is built until it is answered.

Filing gate: ② a decision only the maintainer can make — amending ruling 6415710193 (letter A on #25808, 2026-10-26T12:12Z). The seat ruling on #26601 (6439436943) declined to reopen B′ because a seat cannot amend a maintainer ruling; hotclm's maintainer ruled (hotclm#31, 6396322580, letter C) to wait for the platform rather than stamp durations app-side, so a "no" here is also an answer that goes back to that maintainer. Every reading below is on origin/main 1685a06d (fetched 2026-10-27).

Governing text:

Ruling-ref: 6415710193
Related: #25808 (parent, ruling A) · #26601 (closed via PR #26655, seat ruling 6439436943) · #15103 (ruling 5548479553, PR #15781) · #7598 (ruling 5261751132) · #14104 / PR #15102 (addDays) · PR #26261 (the $now anchor) · hotclm#31 (6396322580 C) · hotclm PR #168

Reader who acts: the maintainer rules. On A nothing moves in this repository; hotclm is told the platform answer. On B′ this card becomes the execution card (domain:spec, with driver-sql and formula hunks), and the three standing texts above get a new ruling line.

维护者速读

选项 做什么 用户看到 工作量
A 维持(推荐) 平台不动。hotclm 在 clm_contract 上声明一个数值字段 review_sla_days,expression: record.contract_type.review_sla_days(平台在每次写入时沿一跳自动算、自动存,不用写 hook);看板过滤写 review_started_at < { $now, addDays: { $field: 'review_sla_days' } } 看板正常计数;合同类型的 SLA 改了,已有合同要等下次写入才跟上(按「审查开始时生效的 SLA」理解) 平台 0(#26261 +2587/−95、35 文件,#26655 +414/−39、10 文件,均已合并)。hotclm S:一个字段 + 一个度量,本会话无 hotclm 仓访问权,未实测
B′ 放开一跳 偏移量允许写 a.b(一跳 to-one 关联,lookup / master_detail),driver-sql 为这个偏移量编一个 LEFT JOIN;数据过滤、数据集过滤、度量过滤同一条规则 看板直接读类型上的 SLA,类型改了立即生效 domain:spec + driver-sql + formula 一个 PR、合同审查档;按 6414128401 读数 ≈ A 再加 300–500 行源码、1.5–2 倍、4–5 dev-day;跨表 JOIN 在两条路径上都无落地先例(#15103 实测后选了回退 B,PR #15781 只钉了边界);并且推翻 2026-08-06「不做 JOIN」与 2026-09-05 #15103 回退两条既裁
  • 业务含义: A = Salesforce 的做法:合同上放一个跨对象公式字段 Contract_Type__r.Review_SLA_Days__c,报表过滤读合同自己的字段;SOQL 的 WHERE 本来就不能「日期 < 今天 − 父记录的天数」。B′ = 让过滤条件自己去 JOIN 父表取参数,主流平台没有这个形状。
  • 推荐 A。 只看长远:三条既裁(08-06、08-12、09-05)都把跨列比较钉在「同表、一处执行」,B′ 是第四次问同一个问题,而且要在引擎路径里新开一个 JOIN 契约;A 的形状平台今天已经能声明(存储表达式一跳),不需要应用写 hook。盲区:存储表达式沿一跳取值这条在引擎 docblock 里写明、未找到逐行测试钉子(见前提 6)。
  • 请回: A / B′。

选项细节

A — keep ruling A; the author-time refusal (#26655) stays. The platform contract is as filter.zod.ts:189-193 states it. hotclm's half, in platform-native form: a stored computed field on clm_contract — review_sla_days: { type: 'number', expression: record.contract_type.review_sla_days }. Per packages/objectql/src/engine.ts:3028-3040 (planStoredExpressions, #24619) a non-formula field with an expression has a column, is computed by the engine "on every write — the declared expression over the row the write stores", and is planned "with the formula read plan's own entry builder … so the scale rounding and the one hop are the read's"; both write paths wire relatedFor: resolveFormulaRelated (engine.ts:14912, :16324-16330). So the copy ruling 6415710193 item 6 asks for needs no hook: it is a declared field, recomputed on insert and on every update by id, and on a predicate update only when the patch moves contract_type (field.zod.ts:1869). The one business caveat: a change to clm_contract_type.review_sla_days reaches an existing contract on its next write, not immediately — which reads as "the SLA in force when the review started", a defensible SLA semantic, but it is a semantic and the card says so. The measure filter is then review_started_at: { $lt: { $now: true, addDays: { $field: 'review_sla_days' } } } with the column holding negative days, the spelling content/docs/protocol/objectql/query-syntax.mdx:545-547 teaches.

B′ — compile a to-one path offset. Strategy (ii) of reading 6414128401: the engine path (driver-sql) learns a one-hop LEFT JOIN for the offset operand only — applyCrossFieldComparison (sql-driver-cross-field-comparison.ts:350-354) and applyNowAnchorComparison (:515-519) resolve a.b through the declared lookup / master_detail field a's reference, join that table on its primary key, and bind COALESCE("a"."b", 0) into crossFieldOffsetExpr. Enforcement stays in one place (the driver), so the 2026-08-12 rule's reason survives; what it reverses is the 2026-08-06 sentence "no JOIN planning, no alias contract" and the 2026-09-05 #15103 fallback ("capability unchanged … a dataset's include does not widen it", query-syntax.mdx:589-595). Strategy (i) — a cross-field compile branch inside NativeSQLStrategy riding ADR-0071's qualifyAndRegisterJoin — is not proposed: it is option A of the 2026-08-12 ruling, rejected there by name ("为无实测消费者的钩子建第二套枚举面"), and it would leave ordinary data filters refusing what dataset filters accept. Under B′ the spec (filter.zod.ts:295-300 AddDaysReferenceSchema regex, filter-comparand-type.ts:295-311 dottedAddDaysOffset / dottedAddDaysOffsetRefusal) narrows its refusal to two-or-more hops and to a first segment that is not a declared to-one field; the memory evaluator (@objectstack/formula matches-filter.ts) must either expand the hop or refuse, because today it reads a lookup's id on a stored row and counts zero days (PR #26655 body, "Executors measured"); driver-mongodb keeps refusing; cross-field-conformance-cases.ts gains the relation cells on both paths; validate-filter-tokens and the docs follow. Workload: one PR at CONTRACT_REVIEW_TIER, Clause-②: yes (widening), Metadata-protocol: added; per 6414128401 ≈ A + 300–500 source lines, 1.5–2× A, 4–5 dev-days; measured unit PR #26261 (+2587/−95, 35 files, merged 2026-10-26T19:11Z). No landed precedent for a join in either cross-field path — 「无先例」; the nearest measurement is #15103 → PR #15781, which measured the join chain and answered that it does not reach the where compilation.

Why there is no third letter. A declared formula field is virtual and cannot be the offset: packages/spec/src/data/filter-cross-field-comparison-class.ts:145 lists formula in CROSS_FIELD_NO_CLASS_REASONS ("virtual: no stored column"). A summary roll-up aggregates children, and clm_contract_type is the parent. The stored-expression field above is the only platform-native route, and it is A's execution, not a new contract — so it is written under A rather than as C. The documented "stamp a due date" route is hotclm#31's letter C, declined there; not re-offered.

Premises, each with its re-check

  1. The dotted offset is refused at save and os validate — git show origin/main:packages/spec/src/data/filter.zod.ts | sed -n 295,300p (regex /^[^.]*$/ on AddDaysReferenceSchema.$field); git grep -n 'dottedAddDaysOffset' origin/main -- packages/spec/src/data/filter-comparand-type.ts → :295, :415. Holds.
  2. Every SQL read refuses it — git show origin/main:packages/drivers/driver-sql/src/sql-driver-cross-field-comparison.ts | sed -n 350,354p (pair offset), | sed -n 515,519p (now-anchor offset), | sed -n 268,272p (bare reference). Holds. (Seat ruling 6439436943 cited :270; that is the bare arm; the offset arms are :350 and :515 on this head.)
  3. The native strategy declines every cross-field comparand so the engine path compiles it — git grep -n 'findCrossFieldComparand' origin/main -- packages/services/service-analytics/src/strategies/native-sql-strategy.ts → :38, :1063, :1084; ruling quoted at :666-672. Holds.
  4. The engine path has no join to ride — git show origin/main:packages/services/service-analytics/src/strategies/objectql-strategy.ts | sed -n 1142,1162p ("the engine cannot join in an aggregate"); executeAggregate carries no include (include-relation-cross-field-boundary.test.ts:28-33). Holds.
  5. Dataset.include is to-one paths ≤3 hops — git show origin/main:packages/spec/src/ui/dataset.zod.ts | sed -n 595,606p. Holds.
  6. A stored expression on a non-formula field reads one hop at write time — git show origin/main:packages/objectql/src/engine.ts | sed -n 3028,3040p; relatedFor wired at :14912 (insert) and :16324-16330 (update). Docblock-stated; engine-stored-expression-write.test.ts carries no one-hop case (git grep -n -i 'hop' origin/main -- packages/objectql/src/engine-stored-expression-write.test.ts → 0 hits) → the row-level behaviour is 「未验证」; a dev on A's hotclm side verifies it with one record before relying on it.
  7. hotclm's ask — objectstack-ai/hotclm is not attached to this session (REST 403), so the ask is cited as Analytics: the $now anchor's addDays offset cannot read a related record's column (SLA per contract type) — and os validate passes the dotted path the runtime refuses #26601 quotes it: nine contract types, review_sla_days ∈ {2, 3, 5, 10}, measured on @objectstack 18.2.0 from hotclm PR 🔗 Broken links detected in documentation #168 as the head of legal; hotclm#31 ruling 6396322580 C. 「未验证」 against hotclm itself.

The four axes

Self-check: 只看①选 A;②③④ 是否翻转:否。

os-decision-facets

Prior rulings read: addDays dotted / $field JOIN same-table / $now anchor → 5 rulings read by id (6415710193, 6414128401, 5548479553, 5261751132, 6439436943) + #26601's dedupe (#25808, #26301); ADR-0021 D-C, ADR-0071; thread: #26601 6439436943 · full-text issue search unavailable in this session (search/issues 403, repo-bound) → unresolved.

裁后执行

Duplicate search. objectstack issues read this round: #25808 (closed, ruling A), #26601 (closed, PR #26655), #15103 (closed, PR #15781), #7598 (closed, PR #7916), #14104 (closed, PR #15102). No open card asks for a path-valued addDays offset. Full-text search was not available to this session (see Prior rulings read).

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Brief-claimed-by: session_01KrKXQVvgYk2AJ4VkqMobyb · batch #415 · 2026-10-27T22:54Z

    Director seat, summon #51 continuation (ledger objectstack#12708). Presented to the maintainer in batch #415 as item 1 of 5 (five spec/server/ui decisions). Seat increment (re-verified on origin/main aa00362fe5; the card is not restated):

  2. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #415 item 1 · letter A · maintainer 「同意」 2026-10-28T00:34Z

    Recorded by the director seat (summon #51 continuation, session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708). The maintainer answered batch #415 with 「同意」 — every seat letter. Brief 6455600109. Letter A — ruling 6415710193 (A on #25808) stands; B′ is declined a second time: the addDays $field offset is a column of the same record; no to-one hop, no driver-planned JOIN for the offset operand; the authoring-door refusal of #26655 stays. hotclm's platform-native route is a stored expression field on clm_contract — review_sla_days: { type: 'number', expression: record.contract_type.review_sla_days } (computed by the engine on every write, planStoredExpressions, engine.ts:3069) — and the dashboard filter reads that column: review_started_at < { $now, addDays: { $field: 'review_sla_days' } }. The hotclm dev verifies the one-hop stored expression on one record first (the card's premise 6 is docblock-stated, not test-pinned); if it does not read the hop, that is a platform defect to file, not a reason for B′.

    Workload: platform 0; hotclm one field declaration (hotclm#31 / PR #168). The three standing texts (filter.zod.ts:86–90, :189–193, native-sql-strategy.ts:666–672) gain no new line.

    Execution: needs-user-decision leaves and this card closes not_planned in this act; a one-line pointer goes on #26601 for the hotclm relay. Ruling-ref: names this comment. Clause-②: no, Metadata-protocol: none.

  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Closed not_planned by the director seat · 2026-10-28T00:45Z

    Ruled A by the maintainer (ruling 6457437597, 「同意」 to batch #415): ruling 6415710193 on #25808 stands and B′ (a to-one hop for the addDays offset) is declined; hotclm's route is a stored expression field on the contract (pointer on #26601, 6457582961). Nothing to build in this repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsdomain:specenhancementNew feature or requestpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions