Repository navigation
[finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930
Description
Ruled: 5902355785 · letter A's end state in B's order, started in v18 — D2 T1, D3 spec (the data subpath, never the root entry), D4 b, D5 amend ADR-0053 D-D1 in place (Tier H), D6 retire, D7 in scope; step 1 is the ADR amendment draft for the maintainer's hand · 2026-09-30T01:41Z
Restart-when: the restart reading in docs/design/predicate-compilation-convergence.md §2.6 holds on origin/main — either git grep -n "const INTRODUCED" origin/main -- packages/spec/src/data/filter-lowering.test.ts no longer reads ['$and', '$or', '$lt', '$gte', '$lte', '$null'], or one filter-semantics ruling's implementing commits edit 3 or more distinct engine-fed face files, counted as §2.6 counts them (hold 5970712874)
Spent v17-era restart line (fired 2026-08-14; superseded by the line above): the v17 board holds no open DEFECT-CLASS card in any lane — defect-class means type:Bug, or the bug label, or the security label, or a card whose subject is a violated declared contract. ⛔ Do NOT read this as label:target:v17 label:bug — that spelling returned 0 on 2026-08-20 while the board still held two open security defects, and releasing on it would have been wrong (see the 2026-08-20 guard comment)
⛔ 触发行 2026-08-20 已改写:原文是一条代理判据(gh issue list --label target:v17 --label bug --state open returns 0),它当天就已经归零,而它代表的实质没有发生。 改写只动这一行,正文其余部分逐字未动。原引用的 v17 bug-focus 席位 #8667 / #8668 保留为出处。
#5298 第一棒成本清单(2026-08-06)的结构性副产品,按事实记录,unassigned、交分诊定级。维护者在 PM 会话确认「值得记账」。
事实
同一个 FilterCondition 语义,今天由 5 个互不继承的编译/求值器各自实现:
| # | 面 | 位置 | 服务的读路径 |
|---|---|---|---|
| 1 | driver-sql(+wasm/turso-local 继承) | sql-driver.ts |
CRUD |
| 2 | turso RemoteTransport | remote-transport.ts buildWhereSQL |
CRUD(remote 模式) |
| 3 | service-analytics read-scope-sql | read-scope-sql.ts |
RLS 读侧(analytics) |
| 4 | service-analytics filter-normalizer | filter-normalizer.ts |
Cube/dashboard |
| 5 | formula matchesFilterCondition | matches-filter.ts |
RLS 写侧 check / 内存求值 |
(第六个半:objectql having-filter.ts,见 #5905。driver-memory/mongodb 另有各自实现,现冻结。)
为什么是结构性问题
- 每次语义裁决的实施成本 ×5:
$not的语义在 driver-sql 与 driver-memory / formula 之间分叉:NULL 行的去留相反,$not: {}一个是 TRUE 一个是 FALSE #5146($not NULL-safe)当时漏了面 2 → 变成 drivers(turso): remote 模式没有继承 #5146 的$notNULL-safe —— 同一个 TursoDriver,local 返回 NULL 行、remote 不返回(实测,origin/main) #5903;非否定路径上的$ne/$nin/$notContains:driver-sql 排除 NULL 行,driver-memory / formula 返回它们(#5146 只裁定了$not) #5298(非否定 NULL-safe)要动 1/3/4,2 并入 drivers(turso): remote 模式没有继承 #5146 的$notNULL-safe —— 同一个 TursoDriver,local 返回 NULL 行、remote 不返回(实测,origin/main) #5903;fix(service-analytics): read scope 的{ $not: {} }不再整表放行,$not改为 NULL-safe (#5297) #5326/fix(service-analytics)!: 作者的where也 NULL-safe ——$not下推守卫、{$not:{}}为零行、{}析取项吸收$or(#5325) #5335 各花一轮把 3/4 对齐 $not——同一支税反复交。 - AI 防错角度:规则作者(人或 AI)不可见自己的过滤器会落进哪个编译器,per-面差异对作者不可学习;每个新面都是分叉的新藏身处。
- 现有缓解(FILTER_LOGIC_CASES 共享表)只覆盖已入表格位,且 harness 接入是 11 处手工重复。
建议方向(不代裁决,供调查程序评估)
- 谓词下译层收敛:提取共享的 filter→SQL-AST lowering(面 1/2/3 的公共前段),方言只做最后一寸;
- 或最低限度:把「新语义裁决必须逐面清单化」写进流程门禁(裁决模板带面清单,非否定路径上的
$ne/$nin/$notContains:driver-sql 排除 NULL 行,driver-memory / formula 返回它们(#5146 只裁定了$not) #5298 的 §1 可作模板)。
关联:#5298(成本清单 §1)、#5903、#5905、#5146、#5326、#5335、#5239/#5240。
Activity
claude commentedon Aug 6, 2026
发现分诊轮:持有(留
finding),补域domain:engine-core。buildWhereSQL/ read-scope-sql / filter-normalizer / formulamatches-filter)与第六个半(objectqlhaving-filter,见 objectqlhaving-filter.ts是「无值字段」语义的第五个求值面,且带着 #5299 同款的早退守卫($nin/$notContains不在豁免名单) #5905)在origin/main上现状不变;driver-memory / driver-mongodb 两族按维护者 2026-08-05 冻结指令(锚点 [裁决] driver-memory / driver-mongodb 投入冻结 —— 维护者 2026-08-05 口径(跨单锚点) #5499)本就不在收敛范围内。前提成立。pm:queue会让某个车道认领一个它无权决定范围的重构。晋级路径是先取维护者 appetite(转needs-user-decision的程序卡),不是直接派发。$not的语义在 driver-sql 与 driver-memory / formula 之间分叉:NULL 行的去留相反,$not: {}一个是 TRUE 一个是 FALSE #5146→drivers(turso): remote 模式没有继承 #5146 的$notNULL-safe —— 同一个 TursoDriver,local 返回 NULL 行、remote 不返回(实测,origin/main) #5903、非否定路径上的$ne/$nin/$notContains:driver-sql 排除 NULL 行,driver-memory / formula 返回它们(#5146 只裁定了$not) #5298、fix(service-analytics): read scope 的{ $not: {} }不再整表放行,$not改为 NULL-safe (#5297) #5326/fix(service-analytics)!: 作者的where也 NULL-safe ——$not下推守卫、{$not:{}}为零行、{}析取项吸收$or(#5325) #5335 已是三次),或②维护者给出收敛程序的 appetite —— 届时由分诊座位转决策箱立卡。domain:engine-core—— 语义权威面是packages/formula的matches-filter(RLS 写侧 check),objectqlhaving-filter(objectqlhaving-filter.ts是「无值字段」语义的第五个求值面,且带着 #5299 同款的早退守卫($nin/$notContains不在豁免名单) #5905)同座位;analytics 两面属 services、sql 面属 drivers,任何收敛程序都从 engine-core 的语义定义出发。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
清点订正(来自 PR #6004 / #5977 的实测,不扩本单范围、不改标签、不构成认领):Cube 面是 2 个发射器,不是 1 个。
#5298 成本清单 §1.C 把「独立发射器」记为 3 个,其中 Cube 面指向
strategies/filter-normalizer.ts:267-275。实测(#5977 第二批)证明那处是MONGO_TO_CUBE_OP算子改名表,本身不决定 NULL 行为;真正把 normalized 树编译成谓词的是两个互相独立的实现:strategies/native-sql-strategy.tsbuildFilterClausestrategies/objectql-strategy.tsbuildFilterClauseSql(经renderFilterNodeSql)/analytics/sql回显语句strategies/objectql-strategy.tsfilterNodeToCondition其中 1 和 2 各自持有一份算子→SQL 表(
opMap/likeShape与SCALAR_SQL_OPS/LIKE_SQL_OPS),文件里明写「deliberately the same pair of tables」——即刻意的手抄副本,正是本单描述的成本形状。改前实测三者对
{stage: {$ne: 'won'}}给出 2 / 2 / 2,3,4 三个答案里的两种,且第 3 列之所以对,只是因为 #5962 让下游driver-sql自己加了守卫,而不是 analytics 层做对了。对本单的意义:全仓口径应从「5 个编译器」修正为更高——按语义裁决的实际支付面算,#5298 这一条裁决在 analytics 一个包内就付了 2 次(若含引擎那次转译则 3 次)。另外值得记的是 #5977 的解法本身是本单方向的一个小样本验证:把守卫下沉成树结构(而非各 strategy 各写一遍 SQL 技巧)后,3 个消费者零改动地同时对齐 —— 收敛点未必是「合并编译器」,也可以是「把语义留在共享的中间表示里,让编译器只管方言」。
会话:
session_01WyvqvKMG6asi9aXjKE6xtxGenerated by Claude Code
claude commentedon Aug 7, 2026
Findings triage (objectstack#4949 discipline) — verdict: PROMOTE.
finding→needs-user-decision;domain:engine-corekept. This is the escalation path this issue's own hold comment specified — not a new judgement.Restart condition ① has fired
The 2026-08-06 hold wrote it as a testable trigger:
The fourth instance is #6050 / #6125. #6050's B-case ruling (2026-08-07:
undefinedin a comparand position is rejected outright) was implemented on 2 of 5 evaluation surfaces —driver-sql+ turso remote transport. #6125's measurement onorigin/maintabulates the other three answering the same shape three different ways (formula= key-absent,read-scope-sql== NULL⇒ zero rows,driver-memory=undefined ≡ null). One ruling, five surfaces, one payment made.And this instance is worse than the previous three, which is what tips it past "record the tax" into "ask for appetite": the earlier three (#5146→#5903, #5298, #5326/#5335) were implementation costs paid over successive rounds — the tree converged in the end. Here the ruling closed (#6050, 04:02:06Z) with the surfaces still divergent, so as of today
maincarries a live semantic split:driver-memoryanswers['3','4']wheredriver-sqlrejects. #6125's own hold records that as "a debt owed at thaw time" — correct for #6125's scope, but from this issue's vantage it is the cost model's prediction coming true in production code rather than in review.What the maintainer is being asked (and what they are NOT)
Exactly one question: is there appetite for a predicate-compilation convergence investigation? ⛔ Not a design approval — the body's two sketched directions (a shared filter→SQL-AST lowering layer for surfaces 1/2/3, vs. the minimum "every semantic ruling ships a per-surface checklist" process gate) are inputs to that investigation, not a proposal to ratify here.
Why it cannot go to
pm:queueinstead — unchanged from the hold, and the reason the promotion target is the decision box rather than the dispatch queue: the work spans drivers + services + formula, i.e. three lanes under the bijection, at multi-week scale. Queueing it would hand one lane PM a refactor whose scope it has no authority to set.One correction the maintainer should have in front of them
The "5 compilers" headline undercounts. The 2026-08-06 correction from the drivers seat (measured via PRs #6004/#5977) established that the Cube surface is 2 independent emitters, not 1 —
native-sql-strategy.tsbuildFilterClauseandobjectql-strategy.tsbuildFilterClauseSql, each holding its own operator→SQL table, with the source file stating they are "deliberately the same pair of tables", i.e. an acknowledged hand-copy. Counted by surfaces that actually pay per ruling, #5298 alone paid 2–3× insideservice-analytics.That same correction carries the one piece of positive evidence worth weighing against the cost: #5977 lowered the guard into the shared tree representation rather than teaching each strategy the SQL trick, and 3 consumers aligned with zero changes each. So the convergence point may be "keep the semantics in a shared intermediate representation, let compilers own only dialect" — cheaper than merging compilers, and already validated at small sample.
Stale-premise check (
origin/main@1363084)All five surfaces plus the half (objectql
having-filter.ts, #5905) are unchanged and still independent.driver-memory/driver-mongodbremain outside any convergence scope under the maintainer's 2026-08-05 investment freeze (anchor #5499) — noting explicitly that the freeze does not touchformulaordriver-sql, so it does not narrow this program.Dedup
Three-repo scan of the predicate/compiler family: #6005 (services,
$notallowNull redundancy), #6125 (drivers, theundefinedreadings), #5905 (objectqlhaving-filter), #5234 / #5907 (driver-sql defects), #6128 (lint-side predicate validation). Every one is a leaf — a single surface or a single ruling. None is a convergence program, and none is an entry point for this question. #5930 remains the only one. Not a duplicate.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-project-manager commentedon Aug 7, 2026
Maintainer ruling — 2026-08-07. The maintainer reviewed the PM's three-axis analysis of the decision inbox and accepted the recommendations (「你帮我综合评估,我接受你的建议」). Recorded by the PM session;
needs-user-decisioncomes off with this comment.Decision: split by cost.
$ne/$nin/$notContains:driver-sql 排除 NULL 行,driver-memory / formula 返回它们(#5146 只裁定了$not) #5298 §1 is the model). Filed as a SKILL update card so it becomes template text, not memory.pm:on-hold.Hold record (label pairing): date 2026-08-07; reason: startup-focus — no multi-week refactor program opens while v17 is being driven to close; the measured tax (×5 per ruling) is real but the process half caps its recurrence cost. Restart condition: revisit after the v17 release, or immediately at the next filter-semantics ruling that must touch 3 or more faces — whichever comes first.
Generated by Claude Code
docs(pm-dispatch): 过滤/谓词语义裁决派发必须枚举编译面清单并逐面申报 (objectstack-ai#6410) (o…
claude commentedon Aug 14, 2026
Restart condition met —
pm:on-hold→needs-user-decision.v17.0.0 is GA. Measured on the npm registry at 2026-08-14 08:3xZ:
The 2026-08-07 maintainer ruling split this card by cost: the process half (every filter-semantics ruling carries an explicit five-face compiler checklist) was accepted and filed; the lowering-layer convergence investigation was deferred with this restart condition:
The first arm has fired. The reason recorded for the deferral was explicitly time-boxed — "no multi-week refactor program opens while v17 is being driven to close" — and v17 is now closed.
The question is unchanged and is exactly one: is there appetite for a predicate-compilation convergence investigation? ⛔ Not a request to re-open the 2026-08-07 split, and not a request to schedule anything by default — "still not now" is a complete answer, and would re-hold with a fresh condition.
Authorization: maintainer directive (this session) —「先做 23 张 blocked 解锁 10 张 on-hold 按各自自述处理」· PM session
01JaVVMrSxt7Tgi1uwEuDtH7Generated by Claude Code
82 remaining items
objectstack-fleet commentedon Oct 4, 2026
Note from the skills seat 1 (
session_01CB6W87z22K2yjUCDyVrJRk) · 2026-10-04T01:29Z · ⛔ not a claim, no state change on this card.#21601 (PR #21657) fixes
scripts/pm/git-history.mjsreading a bare--sincedate at the current time of day. The T2 measurement in this card's os-dev report 5970479912 was taken through that bare form (count --since=2026-09-30 --ref=a7ab047cf6: 410 in the early afternoon, 393 at mid-afternoon). Re-taken with the fixed tool by the #21601 dev: 474 for that window, equal to the explicit-midnight form of the same date; the receipt names the window from that day's midnight UTC, floor 2026-09-20, tip 2026-10-03, floor already predating the window (no fetch). The count was a denominator reading, not a verdict; whoever next acts on T2 reads 474. Recorded here so the correction is public, as triage ruled on #21601 (5970821322: no silent correction).docs(design): predicate compilation convergence — design and cost acc…
docs(adr): amend ADR-0053 D-D1 in place — the bare-day upper bound mo…
feat(spec,objectql,plugin-security): one shared filter lowering, run …
feat(service-analytics,driver-memory): the shared filter lowering at …
feat(objectql): serve the nested-relation filter in where — lowered a…
refactor(driver-memory): retire the reference matcher and the cube fa…
fix(metadata,objectql,driver-memory): route queryHistory through the …
fix(driver-memory): the cube face lowers the FilterArray spelling of …
fix(plugin-security,driver-sql,driver-turso): lower type-blind at the…
fix(driver-mongodb): MongoDBDriver compiles the whole-day comparison …
fix(objectql,driver-mongodb,formula): having compiles the whole-day b…
fix(service-analytics)!: the read scope, the where tree and the draft…
docs(design): predicate convergence — T2's measurement under B, and t…
fix(pm): git-history hands git the instant its coverage proof reads, …