Repository navigation
Analytics datasets: no filter compares a datetime column against now minus an SLA held on a related record #25808
Description
Ruled: 6415710193 · letter A · 2026-10-26T12:13Z
Filing gate: ③ maintainer-directed task, split from #25778 (item 2) as its triage asked.
Reader: domain:spec seat, the next PR on the filter comparand grammar (packages/spec/src/data/filter.zod.ts, $field / addDays) and its two executors (@objectstack/driver-sql filter compiler, matchesFilter in @objectstack/formula), then the dataset measure filter that reads it.
Dedupe (open AND closed): "filter compare datetime against now minus days from related object field SLA per type" → 1 hit (#21255, closed, unrelated); "relative date offset from a field value, $field addDays now, cross-object reference in filter" → 2 hits (#21255, #20127, both closed, unrelated). Nearest prior art: #14104 (closed), the addDays ruling, whose anchor is a column, never now.
Why (the maintainer's words stand in)
Ruling, hotclm#31 comment 6396322580, letter C: keep the goal, file the capability, do not stamp durations app-side.
Triage on #25778 (comment 6397322215): "A datetime-difference measure in the semantic layer, with no SQL or expressions (ADR-0021). Comparing against another object's column is a second, separate ask; split it if the dev finds it needs its own key."
The case behind it: a contract-review dashboard counts reviews past their SLA, where the SLA in days depends on the contract's type: clm_contract.review_started_at older than now minus clm_contract_type.review_sla_days. The same shape is any "overdue by type" count (support response SLA by priority, supplier response SLA by category).
The ask
A per-row predicate, usable as a dataset measure filter and a dataset filter, that compares a datetime column against now minus N days, where N is a numeric column of a related record (a lookup / master-detail parent). Or, if that is ruled out, a documented statement that this needs a column the app stamps, so apps know which half is theirs.
Why the #25778 PR does not cover it, and why it needs its own key
The #25778 PR adds a duration: { from, to, unit } measure: the time between two datetime columns of the same record, aggregated. Item 2 falls outside it on two counts, and neither is reachable with today's keys:
- Now as the anchor, with a column offset.
addDays(An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104) puts a whole-day offset on a{ $field }comparand, socompleted_at <= due_date + grace_dayscompiles. But the anchor is always a column; the now-anchored tokens ({N_days_ago}and kin) take a literal N. "now minusreview_sla_daysdays" has no spelling. - The offset lives on a related record. SQL push-down compiles
$field(and a$fieldoffset) as a same-table column only; a dotted path is refused with400 INVALID_FILTER(the 2026-08-06 rulings recorded infilter.zod.ts, "Execution support ([spec]$field跨字段比较:spec 声明 + cel-to-filter 产出,但无任何 SQL 执行层实现 —— enforce-or-remove 裁决位 #5041 → [spec] SqlDriver 将$field编译为列对列比较(cross-field comparison push-down) #5222 → An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104)"). The in-memory evaluator follows dotted paths, so the two paths would disagree if one were simply allowed.
So either a now-anchored comparand that takes a { $field } offset, plus a cross-object reading for it (a JOIN contract the #5222 rulings declined), or the documented "stamp it" answer. That choice is the maintainer's; this card states what is missing.
⛔ No expression language or raw SQL (ADR-0021), as for #25778.
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
priority:p1·domain:spec(with the filer'senhancement·area:reports) ·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T15:58Z⛔ Not a claim, ⛔ not a dispatch.
driver-sqlandmatchesFilter). No SQL or expressions (ADR-0021).Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Escalated to the decision box ·
domain:specseat 2 ·zhuangjianguo· sessionsession_01RyMY3prh9GRva1Tt37Di3i· 2026-10-26T06:33Z⛔ Not a claim, ⛔ not a dispatch.
pm:queue→needs-user-decisionin the same stroke as this comment. Why the seat escalates instead of dispatching: the card itself states the choice is the maintainer's ("either a now-anchored comparand … plus a cross-object reading … or the documented 'stamp it' answer"), and one option reverses a standing maintainer ruling (same-table only, no JOIN). A dev cannot settle that by reading code.维护者速读
addDays只能挂在某一列上,不能挂在「现在」上;$field在 SQL 下推里只认同表列,带点的跨表路径一律 400 拒收(2026-08-06 裁决)。Governing text
packages/spec/src/data/filter.zod.tsdocblock "Execution support ([spec]$field跨字段比较:spec 声明 + cel-to-filter 产出,但无任何 SQL 执行层实现 —— enforce-or-remove 裁决位 #5041 → [spec] SqlDriver 将$field编译为列对列比较(cross-field comparison push-down) #5222 → An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104)": the maintainer's 2026-08-06 rulings bound SQL push-down to "same-table columns only (a dotted path is refused — no JOIN planning, no alias contract)".addDays— a whole-day offset on the referenced column (An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104, ruled 2026-09-02)": the offset sits ON a{ $field }reference; "{N_days_ago}is anchored to NOW, never to a column".docs/adr/0021-analytics-dataset-semantic-layer.md): no SQL or expressions in the dataset layer.Protocol declaration: options A and B add a comparand to the published filter grammar (
Clause-②: yes (widening),Metadata-protocol: added); C changes no protocol.Premises, each with its re-check
git grep -n "addDays" origin/main -- packages/spec/src/data/filter.zod.ts(hits, the control) andgit grep -n -i "now.*addDays\|\$now" origin/main -- packages/spec/src/data/filter.zod.ts→ 0 at1d2a246c(read 2026-10-26).$field. Re-check:git grep -n "a dotted path is refused" origin/main -- packages/spec/src/data/filter.zod.ts→ 1.duration: { from, to, unit }, PR feat(spec,analytics,lint,platform-objects,app-shell): a dataset measure can aggregate the time between two datetime fields of each record #25903, open) does not cover this. Re-check: PR feat(spec,analytics,lint,platform-objects,app-shell): a dataset measure can aggregate the time between two datetime fields of each record #25903's file list has nofilter.zod.ts.Options
{ review_started_at: { $lt: { $now: true, addDays: { $field: 'review_sla_days_neg' } } } }(spelling is the dev's to propose; sign rules followaddDays). Both executors (driver-sql,matchesFilter) and the shared conformance corpus. The app keeps the SLA days on the contract (copied from the type when the contract is typed).domain:spec· 1 PR (spec S + driver-sql M + formula S + corpus) · no measured precedent;addDays(#14104) is the nearest shape.addDays: { $field: 'contract_type.review_sla_days' }), compiled as a JOIN in SQL push-down.review_due_atdatetime at review start, then filterreview_due_at < now(works today).业务含义: A = 平台给「距今多少天」加一个可读本行配置的刻度,配置要放在被统计的那行上;B = 平台替你跨表去取配置,代价是在 SQL 编译器里引入关联查询;C = 平台不动,每个应用在流程里自己算好到期时间。
Four axes (business view)
addDays的对称补全(列锚已有,现在锚缺),不扩大 2026-08-06 定下的同表边界;B 在过滤编译器里开 JOIN,是一类新的长期维护面;C 把同一个算术推给每个应用。os validate响亮拒收;C 要求 AI 在流程里写日期算术,写错时看板静默少算。Recommendation: A
裁后执行
Under A: this seat dispatches one card thread (spec key + both executors + conformance corpus),
Clause-②: yes (widening),Metadata-protocol: added, contract-review tier before the queue. Under B: the same, plus a ruling record amending the 2026-08-06 boundary infilter.zod.ts. Under C: a docs-only PR on the dataset/filter pages, and hotclm#31 is told the platform answer.os-decision-facets
Prior rulings read: adddays,days_ago,now-anchored,cross-object,sla → 5 hits; ADR-0034 Decision §4, ADR-0044 D1, ADR-0061 D4, ADR-0061 D6, ADR-0061 D7 (all off-topic: transactions, approval send-back, record search); thread: none; repo: objectstack-ai/objectstack
Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Brief-claimed-by: session_01KrKXQVvgYk2AJ4VkqMobyb · batch #395 · 2026-10-26T10:31Z
Director seat, summon #51 continuation (ledger objectstack#12708). Presented to the maintainer in batch #395 as item 3 of 3. Seat increment — one reading changes the shape of option B, so the seat's own letter is set out below rather than the escalation's:
origin/main:filter.zod.ts:93–98(addDayssits on the referenced column;{N_days_ago}is anchored to now, never to a column) and the "a dotted path is refused" sentence hold; PR feat(spec,analytics,lint,platform-objects,app-shell): a dataset measure can aggregate the time between two datetime fields of each record #25903 (the Analytics datasets: no measure for the difference between two datetime columns (cycle time, per-stage duration, SLA overrun) — #16737 refused the wrong path, nothing provides the right one #25778durationmeasure) merged with 0 changes tofilter.zod.ts.Dataset.includetakes to-one relationship paths up to 3 hops, a dimension/measure field may reference any field along a declared path, the compiler emitsLEFT JOINs — and it is landed (dataset.zod.ts:590,service-analyticsinclude-relation tests). The 2026-08-06 same-table ruling infilter.zod.tsgoverns the generic data-filter push-down (driver-sqlfind), not the dataset compiler. So the escalation's cost line for B ("reverses the 2026-08-06 ruling, ≥3× A, a JOIN contract") does not hold inside a dataset: the join contract exists there already. What does hold:analytics-service.ts:4176routes a dataset filter that carries a{ $field }comparand to the engine path, where the same-table rule applies — so a path-valued offset needs the dataset layer to compile the now-anchored comparand itself (native strategy) instead of handing it to the engine. Cost not measured; it is one compile branch beside joins that already exist, not a new JOIN contract.{ $field }that, inside a dataset filter or measure filter, may be a declaredincludepath (contract_type.review_sla_days), compiled by the dataset layer along ADR-0071's join; in a generic data filter the same spelling stays refused (the 2026-08-06 boundary unchanged). Two years out the dataset layer reads configuration where it lives (ADR-0071's own sentence); A makes every app copy the SLA days onto each counted row — a duplicated column the platform then has to keep in sync, the shape Prime Directive [WIP] Fix error in step four of the action run #5 calls a workaround. ② pull holds for either; ③ both declarative and loud; ④ B′ adds the same one comparand as A, and no new contract beyond ADR-0071. Self-check: 只看①选 B′;②③④ 是否翻转:否.analytics-service.tsaround:4176and the native strategy's join registration; name the function); ③ if it cannot without a JOIN contract outside ADR-0071's declared paths, the dev reports a fork and A (same-record column) is the fallback the maintainer can pre-authorise — ⛔ no forcing, ⛔ no silent retreat to C.Prior rulings read:
addDays·dotted path is refused·ADR-0071 include·ADR-0021→ 4 hits (filter.zod.ts rulings 2026-08-06 and An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104; ADR-0071 Decision — multi-hop to-one; ADR-0021 no SQL/expressions); thread: 6409808781.objectstack-fleet commentedon Oct 26, 2026
Workload and performance reading for the maintainer · director seat · 2026-10-26T10:46Z
Asked in the summon chat after batch #395 (「25808 帮我评估具体的开发工作量以及对性能的影响」). Read on objectstack
origin/main(cdd4eea9), with the landed precedent PR #15102 (theaddDaysoffset, #14104: +1816/−36, 17 files, merged 2026-09-03T23:23Z) as the measured unit. This supersedes the seat's increment 6413864417 on one point, stated first.Correction to the seat's B′ reading. B′ assumed a path-valued offset inside a dataset could ride ADR-0071's joins without reversing a ruling. The code says otherwise: since the 2026-08-12 ruling
NativeSQLStrategyDECLINES every cross-field{ $field }comparison so it routes to the ObjectQL engine path, "so those rules are enforced in one place, next to the metadata they read" (packages/services/service-analytics/src/analytics-service.ts:4176–4205); the engine path isdriver-sql, whoseapplyCrossFieldComparisonrefuses a dotted reference (sql-driver.ts:18030, the 2026-08-06 same-table ruling). So B′ reverses one of the two rulings whichever side compiles it: (i) the native strategy grows its own cross-field compile branch (two enforcement places, against 2026-08-12), or (ii) the engine path learns a one-hop join for the offset (against 2026-08-06). Neither is "no reversal". The seat's letter is therefore A; B′ stays on the table only with a ruling record amending one of those two sentences.Workload
packages/spec/src/data/filter.zod.ts{ $now: true, addDays: <int | { $field }> }; spelling the dev's), describe + refusal prose; regenauthorable-surface{N_days_ago}resolves today —resolveFilterTokensin the engine andtemporal-comparand-door.ts— to a literal instant, leaving the offset reference for the executor; the{ $field }pair validation (engine.ts:21700,having-filter.ts:91) gains the class rule "no base column, offset NUMERIC only"driver-sqlliteral_now + offset_col days—crossFieldOffsetExpr(sql-driver.ts:18249) already spells the day arithmetic on all three dialects (sqlitestrftime … ' days', mysqldate_add … interval … day, pgmake_interval) withCOALESCE(offset, 0)and truncation; the same-table/tenant/declared checks ofapplyCrossFieldComparisonapply as they are@objectstack/formulamatches-filter.tsresolveDayOffset+addWholeDaysexist (:948–961); the now anchor is one resolverdriver-mongodb$fieldcomparands incl.addDaysare already classified (mongodb-filter.ts:486); one case to pin (compile or refuse)service-analytics$fieldcomparand doesNativeSQLStrategyfor this comparand with its own class/tenant checks (duplicating driver-sql's), join viaqualifyAndRegisterJoin; parity tests native ↔ engine (infer-cube-where-spelling-parityis the existing shape); a ruling recordvalidate-filter-tokens/ preset comparands learn the shape;content/docsfilter + query-syntax pages (precedent +64 lines);cross-field-conformance-cases.tscells on both paths (precedent +343)clm_contractcopied from the type on create / type change (hook or lookup-copied field): S in hotclmdomain:specwith driver-sql / formula hunks, contract-review tier; 2–3 dev-daysPerformance
review_started_at < now − sla_days dayshas a right-hand side that varies per row, so no index range scan onreview_started_atcan serve it (same on pg / mysql / sqlite). That is the same class as today'saddDayscolumn offset, and it is harmless where this card uses it: a dataset measure or dataset filter aggregates over the tenant-scoped row set anyway (COUNT/GROUP BYscans those rows with or without this predicate), so the added cost is one date arithmetic per scanned row — microseconds; at 10⁵ contracts, tens of milliseconds at most, under the aggregate's own cost. It matters only if the same predicate became the sole selective filter of a list view on a large table, which is not this card.review_due_at < now) is sargable — that is the one performance argument for C, and it buys nothing for an aggregate.LEFT JOINto the type table per query (primary-key lookup on a small table, hash or nested loop): the same join ADR-0071 already emits whenever a dimension names a path; negligible. Under (ii) the join moves to the engine path with the same cost.$fieldcomparand already sends a dataset query to the engine path (2026-08-12); for SQL drivers that path still compiles the WHERE in SQL throughdriver-sql(not re-read line by line here — stated as the ruling's own description), for the memory driver it evaluates per row inin-memory-aggregation.ts/matches-filter.ts, O(rows), where every dataset already runs.COALESCE), so a contract with no SLA days counts as overdue the momentreview_started_at < now; the dev pins that cell and the docs say it.维护者速读
addDays,+1816/−36、17 文件)的六到八成:spec 一个比较值 + 引擎的现在锚解析(复用{N_days_ago}的解析点)+ driver-sql 复用已有的三方言天数偏移表达式 + formula 复用已有的偏移解析 + 语料与文档,约 400–500 行源码、一个 PR、2–3 个 dev-day;应用侧 hotclm 要在合同上加一列 SLA 天数(S)。B′(偏移取关联记录)= A 再加数据集原生策略一条编译分支与双路径一致性测试,约 1.5–2 倍、4–5 个 dev-day,并且无论放在哪一侧都要推翻一条既有裁决(2026-08-12「跨列比较只在引擎路径一处执行」或 2026-08-06「SQL 下推只认同表列」),本席上一轮「B′ 不翻既裁」的前提被代码证伪,改荐 A。25808 A或25808 B′。objectstack-fleet commentedon Oct 26, 2026
Ruling: batch #395 item 3 · letter A · maintainer 「A」 2026-10-26T12:12Z
Recorded by the director seat (summon #51 continuation,
session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708; brief 6413864417, workload and performance reading 6414128401). The maintainer answered 「A」 after that reading. Letter A: the filter grammar gains a now-anchored comparand with a whole-day offset read from a numeric column of the same record (spelling the dev's to propose, e.g.{ $now: true, addDays: <integer | { $field }> }; sign rules asaddDays), usable as a datasetfilterand a dataset measurefilter, and as an ordinary data filter under the same rules. Both standing rulings hold unchanged: SQL push-down compiles same-table columns only (2026-08-06,filter.zod.ts), andNativeSQLStrategydeclines every cross-field comparand so the engine path enforces the cross-field rules in one place (2026-08-12,analytics-service.ts:4176). Not B′ (reverses one of those two) and not C (reverses hotclm#31 C).What the dispatch carries (from the reading 6414128401):
packages/spec/src/data/filter.zod.ts: the comparand shape, describe and refusal prose,authorable-surfaceregenerated;Clause-②: yes (widening),Metadata-protocol: added,minoron@objectstack/spec.{N_days_ago}resolves (resolveFilterTokens/temporal-comparand-door.ts) to a literal instant; the{ $field }pair validation (engine.ts~21700,having-filter.ts~91) gains the class rule "no base column; the offset column is NUMERIC, declared, same-table, never the tenant column".driver-sql:literal_now + offset_col daysthroughcrossFieldOffsetExpr(sql-driver.ts:18249) on all three dialects, withapplyCrossFieldComparison's checks as they are; NULL offset = 0 days (COALESCE), pinned and documented — a row with no SLA days counts as overdue once the anchor passes.@objectstack/formulamatches-filter.ts: the now anchor besideresolveDayOffset/addWholeDays;driver-mongodb: one pinned cell (compile or refuse).cross-field-conformance-cases.tscells on both paths; lint (validate-filter-tokens/ preset comparands);content/docsfilter and query-syntax pages.review_sla_daysonclm_contract(copied from the type on create / type change); hotclm#31 is told the platform answer.Workload of the chosen letter (reading 6414128401): about 0.6–0.8 of precedent PR #15102 — ~400–500 source lines, one PR in
domain:specwith driver-sql / formula hunks, contract-review tier, 2–3 dev-days. Performance: the predicate is non-sargable on the datetime column like today'saddDaysoffset; in a dataset aggregate it adds one date arithmetic per scanned row, under the aggregate's own cost.Execution:
needs-user-decision→pm:queuein this act (domain:spec, p1,area:reports). The spec seat dispatches one card thread withRuling-ref:naming this comment; the dev verifies premises 1–3 of the escalation 6409808781 first (no now-anchored column offset today; dotted$fieldrefused; PR #25903 touched nofilter.zod.ts) and reports a fork if any fails — ⛔ no forcing, ⛔ no path-valued offset slipped in.objectstack-fleet commentedon Oct 26, 2026
Premise check before dispatch · domain:spec seat 2 · 2026-10-26T15:40Z
This is a read-only premise reading on origin/main
6b9355299for ruling 6415710193 (letter A). The card's goal is unchanged. The readings below move only the landing point, and the dispatch order carries them.Holds; two paths corrected.
packages/spec/src/data/filter.zod.tshas no now-anchored comparand.addDays(:66, :93-98) offsets a referenced column, and{N_days_ago}is anchored to now, never to a column (:97).filter.zod.ts.$fieldis refused, is for the dev to re-verify, as the ruling asks.crossFieldOffsetExpris atpackages/drivers/driver-sql/src/sql-driver.ts:18168, not:18249. Its caller is at :18059.packages/objectql/src/temporal-comparand-door.ts.packages/formula/src/matches-filter.ts,resolveDayOffsetis at :975 andaddWholeDaysat :1003.Each line number above is a re-measurable reading, not a ruling; the dev re-measures before relying on it.
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 2 · 2026-10-26T15:43Z
Session:
session_01RyMY3prh9GRva1Tt37Di3iAccount:
zhuangjianguoBranch:
claude/project-thread-pgsc75(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-25808-<slug>)Worktree:
objectstack-issue-25808Domain:
domain:specSeat:
domain:spec#2File surface:
packages/spec/src/data/filter.zod.ts (+ authorable-surface), packages/objectql/src (temporal-comparand-door.ts, engine.ts {$field} pair validation, having-filter.ts), packages/drivers/driver-sql/src/sql-driver.ts, packages/formula/src/matches-filter.ts, packages/drivers/driver-mongodb (one cell), cross-field-conformance-cases.ts, packages/lint (validate-filter-tokens), content/docs filter and query-syntax pages, one changeset(stop on breach; explain in the report)Container & model:
M,mode:thread,model: CONTRACT_REVIEW_TIER (reason: ruling 6415710193 — a p1 spec widening across spec, objectql, driver-sql, formula and lint; the seat chose the ceiling for the build),effort: highClause-②: yes (widening)
Metadata-protocol: added
Responsibility: n/a — not a defect card
Ruling-ref: 6415710193
Thread-read: 6420234055
Premise: holds (paths corrected) 6420234055
Serial constraints cleared:
noneobjectstack-fleet commentedon Oct 26, 2026
os-dev-report
Written 2026-10-26T17:35Z · PR #26261 · head 9594f37 · branch claude/project-thread-pgsc75
{ "issue": 25808, "status": "PR open (draft) — ready for maintainer review", "branch": "claude/project-thread-pgsc75", "pr": 26261, "head": "9594f3737bf2cf0f4e2dd815372fe13d85e724bd", "session": "https://claude.ai/code/session_019YLF9PyrRqUX4N7C2pzSoH", "premise_still_valid": "yes — premises 1–3 re-verified on origin/main 6b935529 (= merge base) before the first edit: (1) FieldReferenceSchema carries { $field, addDays } and no now-anchored shape; (2) the token resolver knows {now} / {N_days_ago} only as fixed placeholders; (3) driver-sql's crossFieldOffsetExpr exists with the same-table rules. origin/main has since moved to 3d50a43bc; nothing on the claim surface changed there (branch still merges clean, merge base unchanged).", "summary": "Ruling A (comment 6415710193) implemented end to end: `{ $now: true, addDays: <integer | { $field }> }` is a legal comparand of $eq/$ne/$gt/$gte/$lt/$lte on a date or datetime column, in a dataset filter, a dataset measure filter and an ordinary data filter. addDays is REQUIRED (unshifted instant stays `{now}`), sign rules as addDays (no subDays; 'now minus SLA' is a column holding negative days). spec: NowAnchorSchema (closed) in the comparand unions, refused by name as a set member / $between endpoint, equality triple lowers to $eq; comparand-type door and lowering face step around a well-formed anchor and refuse a malformed one in its own words. core: resolveFilterTokens pins $now to the tree's one instant (ISO). objectql: temporal door skips the anchor; aggregations[i].filter (withheld) and having (named) judge it by where's rules and compare per row via the formula evaluator; no engine.ts edit was needed. driver-sql: same-table push-down on SQLite/PostgreSQL/MySQL through crossFieldOffsetExpr (NULL offset = 0 days, documented), instant bound in the target's storage form (datetime keeps time of day; date meets the UTC calendar day), NULL-total predicates so $not is the exact complement; other positions / malformed / non-temporal target / cross-table refused with operands withheld. formula: resolves the anchor in the stored value's form so memory answers SQL's rows. driver-mongodb: refuses the anchor everywhere (INVALID_FILTER). service-analytics: NativeSQLStrategy declines the anchor as it declines { $field } (2026-08-12 rule), routing to the engine path; ObjectQL strategy keeps { $eq: anchor } explicit. Docs: query-syntax.mdx section + regenerated filter.mdx; changeset minor on @objectstack/spec, patch elsewhere. Item 6 (hotcrm) only named in the PR body, per the ruling.", "tests": { "new": [ "packages/spec/src/data/filter.test.ts — [#25808] NowAnchorSchema describe (shape, required addDays, closed object, set-member / $between refusal, equality lowering, malformed-anchor door)", "packages/core/src/utils/filter-tokens.test.ts — [#25808] describe, 6 tests (pinning, one instant per tree, malformed → 400 INVALID_FILTER)", "packages/objectql/src/engine-now-anchor.test.ts — 15 tests (where resolver pin, position refusals, aggregation filter counts + withheld refusals, having class rule)", "packages/drivers/driver-sql/src/cross-field-conformance-cases.ts — CROSS_FIELD_NOW_CASES (12 expectations × 2 pairs + 4 per-class cells) and CROSS_FIELD_NOW_REFUSALS (14), swept per dialect cell by sql-driver-cross-field-conformance.test.ts; 2 DOORS rows in sql-driver-compile-refusal-seam.test.ts", "packages/drivers/driver-sqlite-wasm/src/sqlite-wasm-cross-field-conformance.test.ts — NOW cases + refusals on the wasm cell", "packages/formula/src/matches-filter-now-anchor.test.ts — 25 tests (calendar-day vs instant form, { $field } offset, NULL offset = 0, $not complement)", "packages/drivers/driver-mongodb/src/mongodb-field-reference-refusal.test.ts — [#25808] describe, 10 positions refused", "packages/services/service-analytics/src/__tests__/now-anchor-dataset.test.ts — detector, NativeSQL decline + engine fallback, refusals (author disclosure vs read-scope redaction), dataset measure filter + dataset filter under fake timers, end-to-end dataset", "packages/lint/src/validate-filter-tokens.test.ts — one cell (anchor beside a placeholder)", "packages/spec/src/type-alias-convention.pin.test.ts — pin 789 → 790" ], "suites_at_head": { "@objectstack/spec": "21979 passed, 1 todo", "@objectstack/core": "2784 passed", "@objectstack/formula": "1449 passed", "@objectstack/objectql": "8789 passed", "@objectstack/driver-sql": "3846 passed, 214 skipped (full suite rerun at head after the seam-test enumeration fix; the earlier chain run had 1 failed on that enumeration)", "@objectstack/driver-sqlite-wasm": "746 passed", "@objectstack/driver-mongodb": "714 passed, 182 skipped", "@objectstack/service-analytics": "4646 passed, 282 skipped", "@objectstack/lint": "7385 passed", "live PostgreSQL 16 cell": "sql-driver-cross-field-conformance.test.ts with OS_TEST_POSTGRES_URL (TZ=America/New_York): 318 passed (sqlite + postgres cells)", "MySQL": "NOT run locally (no MySQL in the container); the MySQL cell belongs to main's `Temporal Conformance (live PG + MySQL)` battery which does not run on a PR — the MySQL emission is pinned by the SQL-text cell only" }, "typecheck": "pnpm --filter <pkg> run typecheck exit 0 for spec, core, formula, objectql, driver-sql, driver-sqlite-wasm, driver-mongodb, service-analytics, lint" }, "gates": { "derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths): 120 commands harvested; --ran derivation at the final tree: 122 families", "run": "every command executed sequentially under OS_VERIFY_LOCK_SLOT=issue-25808 with the exit code captured before any pipe (ran-final.list); 121 ran exit 0 after two prerequisite clears — check:skill-examples needed @objectstack/client + client-react built; check-engine-split-ratio --days 90 refused on the shallow clone (exit 2) and passed after `git fetch --shallow-since=2026-07-21` (ratio 97.9%, informational)", "not_measured": "pnpm check:dual-build-cjs-loads — PREREQUISITE NOT MET (needs the whole workspace built, 64 packages without dist); declared with reason in the run record and left to CI", "reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran-final.list --repo objectstack-ai/objectstack → exit 0: 122 derived families accounted for — 121 run, 1 NOT-MEASURED (claimed), 0 UNRUN", "regen_witness": "pnpm --filter @objectstack/spec build && check:generated --fix && check:generated (plain second run exit 0); node scripts/check-nul-bytes.mjs exit 0", "ci": "PR check-runs pending at report time (PR just opened); the thread is subscribed to the PR and will cite the CI families from the check-runs in the seat message / a follow-up when they land. Lock note: the gate sweep held the shared verify lock 28m52s (holder-side starvation warning) — normal for a 120-command sweep, not a finding." }, "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": { "count": 4, "tools": ["mcp__github__issue_read ×3", "mcp__github__pull_request_read ×1"], "note": "all reads; no GitHub MCP write of any kind. Platform (non-GitHub) tool used after PR creation: mcp__claude-code-remote__subscribe_pr_activity ×1 (PR event subscription, not a GitHub write)." }, "api_writes": { "count": 2, "endpoints": [ "POST /repos/objectstack-ai/objectstack/pulls (+ assignees) — relay fleet-write/dispatch.mjs op pr_create, draft forced by the relay", "POST /repos/objectstack-ai/objectstack/issues/25808/comments — relay post-stamped.mjs (this os-dev-report)" ], "note": "git push -u origin claude/project-thread-pgsc75 (not a REST write). No label writes: the changeset publishes, so no skip-changeset." }, "deviations": [ "Files outside the claim's named surface, each the seam the ruling's item required: packages/core/src/utils/filter-tokens.ts (+ test) — the token resolver that pins $now lives in @objectstack/core, not objectql; packages/services/service-analytics/src/{comparand-shape.ts, strategies/native-sql-strategy.ts, strategies/objectql-strategy.ts} (+ test) — the 2026-08-12 NativeSQLStrategy decline named in the ruling; packages/spec/src/data/{filter-comparand-type.ts, filter-lowering.ts} — the FilterCondition door and lowering face that otherwise refused / mis-lowered the anchor; packages/drivers/driver-sql/src/index.ts (corpus export) and sql-driver-compile-refusal-seam.test.ts (builder enumeration pin); packages/spec/src/type-alias-convention.pin.test.ts (pin ledger); packages/drivers/driver-sqlite-wasm conformance test (the corpus sweep's second cell); regenerated spec artifacts + docs references + strictness ledger.", "No packages/objectql/src/engine.ts edit: the {$field} pair validation the claim named is done by the judges engine.ts already calls (having-filter.ts grew them).", "MySQL cell not executed locally (no server in the container) — SQL-text cell only; see tests.suites_at_head.MySQL.", "Commit trailer pair is the repo's model-free form (`Co-authored-by: Claude`), not the harness's model-named trailer, per AGENTS.md." ], "files_changed": 35, "line_budget": "2682 changed lines (+2587 / −95) vs the 3000 human-merge threshold: under", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: @objectstack/driver-memory (not on the claim surface) · evidence: a probe query `{ opened_at: { $lt: { $field: 'other_col' } } }` and the same with `{ $now: true, addDays: -1 }` both answer `[]` silently on driver-memory instead of refusing or evaluating (scratch probe mem-probe-abs.mjs, measured 2026-10-26 at 6b935529) — pre-existing for the { $field } pair, so the anchor inherits the hole · dedupe: driver-memory cross-field comparand silent empty", "carrier: formula `matchesFilterCondition` compares a now-anchored comparand against a non-temporal column as written (string compare) on the RLS write-check seam, which has no declaration map to judge the target class — the declared seams (where/having/driver-sql) refuse; boundary noted in the PR body's acceptance notes, no behaviour claimed there" ] }