You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
$regex —— not in FILTER_OPERATORS, but really produced: plugin-auth's ObjectQL adapter emits { field: { $regex: value } } for a contains search. ... Refusing it here would break a live producer.
范围(第 47-52 行):「Nothing here exercises null handling, dates, numeric coercion, LIKE escaping, or case sensitivity —— those legitimately differ between a SQL engine and a JS matcher, and folding them in would make the table unpassable rather than more useful. Keep it that way」。本单第 2 点要求加的正是这两条被点名排除的轴。
时机(第 54-62 行):「a red row here does not enforce a ruling, it just turns another lane's unfinished work into this table's failure ... Add the rows in the PR that closes the gap, not before.」这与派发口径「红/DEBT 是设计内推进机制」直接相反 —— 而且是这张表在两次实践后自己写下的。
Activity
$regexon driver-sql is not a regex — it compiles to a substring LIKE, so it both over-matches and silently matches nothing #4706os-zhuang commentedon Aug 6, 2026
PM 认领(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06,维护者插队令,P0 第 6 席)claude/issue-5701-regex-retire-icontains;工作树:../objectstack-5701-regex-retire(基于最新origin/main);域:domain:spec$regex/$options退役 +$icontains入表)+FILTER_LOGIC_CASEScase-set + 全仓$regex用面翻转(examples/测试/文档)+ 生成物(走生成器)$regexon driver-sql is not a regex — it compiles to a substring LIKE, so it both over-matches and silently matches nothing #4706 维护者 2026-08-06 裁决 B 案(两条评论为规格);⛔ 驱动半边不做conversions/registry.ts条目(存量元数据内嵌 filter 携$regex的加载缝),停手回报——ADR-0087 别名转换会把「被遮蔽的旧拼法」留在存量元数据里 —— 节点 config 收紧后它从静默丢弃变成执行期硬拒 #4923 在飞同文件,PM 重排;其余在飞面(component.zod/hook.zod/api-routes/kernel-contracts)不触Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
开发前置核验报告(os-dev,
claude/issue-5701-regex-retire-icontains工作树,基于origin/main@eb26126d5)。结论:不提 PR,回报needs_decision。理由分两层:词表半边的前提已失效(可直接销案),case-set 与拒收半边则撞上三处必须由维护者拍板的契约决定 —— 每一处我一旦动手写,就等于替契约做了决定。以下全部为实测,附 file:line。
一、前提核验:
packages/spec里没有$regex,也没有$options本单第 1 点(「filter 算子 zod schema / 类型中移除
$regex(含$options)」)在origin/main上无物可移。逐面核过:$regex/$optionsFILTER_OPERATORSpackages/spec/src/data/filter.zod.ts:957-968FieldOperatorsSchemafilter.zod.ts:178-206StringOperatorSchemafilter.zod.ts:141-153MongoDB: $regex,是 mongo 侧译码说明)AST_OPERATOR_MAP/VALID_AST_OPERATORSfilter.zod.ts:505-586VIEW_FILTER_OPERATORS/VIEW_FILTER_OPERATOR_ALIASESui/view.zod.ts:173-214FilterConditionSchemafilter.zod.ts:320-328z.record(z.string(), z.unknown())—— 不校验任何算子名唯一曾声明过 regex 算子的 spec 面是
api/websocket.zod.ts的FilterOperator枚举,早已被 objectui#2945 删除,墓碑注释(websocket.zod.ts:56-78)还专门写了「将来长出真过滤应 lower 到AST_OPERATOR_MAP,不要再自建词表」。$options的测量(本单要求「实施时核实」):在 ObjectQL 词表意义上,它确实只作$regex的 flags 修饰符存在 ——objectql/having-filter.ts:110直接continue跳过再喂给$regex;driver-memory/filter-refusal.ts:421-435专门为「有$options无$regex」写了悬空错误。但同样地,它也不在 spec 里,所以一样无物可移。$regex今天的真实落点是「一个生产者 + 四个消费者」,全在 spec 之外:packages/plugins/plugin-auth/src/objectql-adapter.ts:137—— better-auth 的contains算子被译成{ $regex: condition.value }。driver-sql/src/sql-driver.ts:6511、driver-turso/src/remote-transport.ts:58,1258+turso-driver.ts:689、driver-memory/src/filter-refusal.ts:171+memory-matcher.ts:209、objectql/src/having-filter.ts:37,140。这四处消费者的代码注释已经写明它们是为那一个生产者而存在的。
driver-memory/src/filter-refusal.ts:154-158原话:这直接给出了一条裁决没点名的时序约束:任何「校验期响亮拒收
$regex」在objectql-adapter.ts:137翻转之前落地,都会打断 better-auth 的认证路径。 该文件既不属契约半边也不属驱动半边,是第三块,须先排。硬约束核验(PM 认领里点名的
conversions/registry.ts):不触发。 退役的不是 authorable key(spec 从未声明该键),ADR-0087 D2 没有对象可登记;且登记处里唯一与 filter 相关的条目是flow-node-crud-filter-alias(conversions/registry.ts:142),它改写的是节点 config 上的filters->filter键名,并不下潜进 filter body 改算子名 —— 存量 view/flow 里内嵌的$regex无法、也从未由登记处触及。本单不需要动conversions/registry.ts,#4923 无冲突。二、实测:
$icontains的方言坑与 A 案的 turso 坑是同一种,只是可解裁决把 B 案排在 A 案之前的理由之一是「
$icontainsLIKE/ILIKE 可实现,无方言正则坑」。这句在大小写折叠的「域」上不成立,已实测。在本仓
better-sqlite3@13.0.2(SQLite 3.53.4)上跑LOWER(col) LIKE LOWER(?)(即$icontains最自然的 SQL 下译),与 JStoLowerCase().includes()(即 driver-memory / formula 的下译)对照:LOWER(col) LIKE LOWER(?)toLowerCase().includes()acme(ASCII)["ascii-up","ascii-lo"]["ascii-up","ascii-lo"]café["accent-lo"]["accent-up","accent-lo"]москва["cyr-lo"]["cyr-up","cyr-lo"]σιγμα["gr-lo"]["gr-up","gr-lo"]SQLite 的
LOWER()/LIKE只折叠 ASCII(无 ICU 编译时),JS 折叠全 Unicode,mongo 的$options: 'i'亦全 Unicode,PostgresILIKE依 collation/ICU。也就是说:「同一条$icontains查询在五后端返回同一结果集」在非 ASCII 上结构性不成立,除非契约明确把折叠域限死。这与 turso remote 注册不了REGEXP是同一类不可行性 —— 区别在于它有解(把域声明为 ASCII),但那个解要付出可见的能力代价,不能由我在写 fixture 时默默选定。而 case-set 的
expected: [...]正是这个选择本身。 我无法在不回答「折叠域是什么」的前提下写出本单第 2 点要求的「双向大小写折叠命中」用例。三、实测:
$contains家族今天已经在同一根轴上分叉,而且是 spec 声明放弃的$contains的大小写行为driver-memoryString.includes)memory-matcher.ts:182formulamatches-filter.ts:182driver-sql/driver-sqlite-wasmLIKE '%v%'—— SQLite 上 ASCII 不敏感、Postgres 上敏感、MySQL 看 collationsql-driver.ts:6292-6304(applyLike)driver-tursoSqlDriver+remote-transport.tsdriver-mongodbmongodb-filter.ts:504-506,$options: 'i'硬写而 spec 对此的表态是
filter.zod.ts:139的一行注释:这不是漏写,是写下来的「不保证」。 它带来两个后果:
$icontains与今天的$contains行为完全相同 —— 新算子在 60% 的矩阵上不提供任何新能力,只是给既有的偶然行为补了个名字;真正拿到新能力的只有 Postgres 与两个 JS 面。$icontains等于推翻filter.zod.ts:139这条已记录的契约表态。按 AGENTS.md Prime Directive [WIP] Add Chinese version of the documentation #13(已记录的决定在被取代前有效,推翻本身是一项决定),这需要一条明写的取代记录,而不是在旁边悄悄加一个算子。四、
FILTER_LOGIC_CASES落点:本单第 2 点与该文件写下的三条规矩正面冲突packages/spec/src/data/filter-logic-conformance.ts的表头(第 37-99 行)自己规定:LIKEescaping, or case sensitivity —— those legitimately differ between a SQL engine and a JS matcher, and folding them in would make the table unpassable rather than more useful. Keep it that way」。本单第 2 点要求加的正是这两条被点名排除的轴。{ field: {} }(零个操作符的字段约束)在同仓有三个答案:driver-sql 组合子内 TRUE、顶层抛 INVALID_FILTER、formula/driver-memory FALSE #5240 家族):「FilterLogicCasehas no way to spell "this filter must be REJECTED" ... Enrolling this case needs the shape extended first (anexpectRejectiondiscriminant, or a sibling table) —— deliberately not invented here」。本单要求的$regex拒收 pin 需要的正是这个被刻意推迟的形状扩展。补充一条机制事实:conformance 门禁(
scripts/check-driver-conformance.mjs)按 import 覆盖判定,不看断言。所以往现有FILTER_LOGIC_CASES加用例,五个 driver 套件会直接判红(不是 DEBT)—— 且 #5702 已写明 driver-memory / driver-mongodb 属 #5499 冻结族、$icontains实现「默认挂起」,即那两格的红没有人被派去清。而新增同级 case-set 则可以用该门禁自带的 DEBT 台账机制记五条实测欠条(temporal-conformance.ts/pagination-conformance.ts已是同级 case-set 的先例),既机械地绑住每个 driver,又不让main变红。五、待裁决的四个问题(附建议)
Q1 ——
$icontains的大小写折叠域?[A-Za-z]),明写进.describe()与文档。五后端都能真交付,「同查询同结果集」是事实。代价必须写明:café匹配不到CAFÉ,中文/欧洲语系应用要知道这条边界。Q2 ——
$contains/$startsWith/$endsWith的大小写语义要不要同批钉死?filter.zod.ts:139的「backend level」表态要不要取代?LIKE改为大小写精确 ——GLOB/instr()/PRAGMA case_sensitive_like均可;mongo 需去掉那个偶然的$options: 'i'),$icontains才是它有意义的折叠孪生。$icontains,$contains维持「backend level」不保证。代价:三个后端上两个算子同义,作者无法从名字判断自己拿到的是哪种;$regexon driver-sql is not a regex — it compiles to a substring LIKE, so it both over-matches and silently matches nothing #4706 的分叉在$contains上原样留存。$contains,不敏感 =$icontains」是可从名字读出的全局规则;B 之下 AI 无从判断。注意 A 会扩大驱动半边的工作量(五后端各改一处$contains下译),请在排期时计入。Q3 ——
$regex的「校验期响亮拒收」落在哪一层?顺序如何?FilterConditionSchema成封闭算子词表(spec 侧真校验)。代价极大:该 schema 今天接受任意$op,且{ profile: { verified: true } }的嵌套关系形与算子对象在结构上不可区分 —— 收紧会波及每一个 filter 消费者,远超本单范围。RETIRED_FILTER_OPERATORS指引表(FIELD_KEY_GUIDANCE/retiredKey的既有惯例,纯数据,合 Prime Directive ✨ Set up Copilot instructions #2),由既有拒收点(driver-memory/filter-refusal.ts、objectql/having-filter.ts、service-analytics、sql-driver的default:)统一引用,五处说同一句话、同一个替代物指名。执行归 drivers:$regex响亮拒收 +$icontains各后端实现(#4706 裁决 B 案 · 驱动半边) #5702。plugin-auth/src/objectql-adapter.ts:137必须先翻转成$contains,任何拒收才能落地 —— 否则 better-auth 的contains查询在认证路径上开始抛错。我已把这块单独立单(见下),它既不属契约半边也不属驱动半边。Q4 ——
$icontains用例落FILTER_LOGIC_CASES还是新同级 case-set?packages/spec/src/data/filter-text-conformance.ts(FILTER_TEXT_CASES),连同该文件刻意推迟的expectRejection判别式一起定义在新文件里,注册进check-driver-conformance.mjs的CASE_SETS并记五条实测 DEBT 指向 drivers:$regex响亮拒收 +$icontains各后端实现(#4706 裁决 B 案 · 驱动半边) #5702。temporal-conformance.ts/pagination-conformance.ts的同级 case-set 先例一致,尊重了FILTER_LOGIC_CASES的宪章,机械绑定力不打折,且main不变红。{ field: {} }(零个操作符的字段约束)在同仓有三个答案:driver-sql 组合子内 TRUE、顶层抛 INVALID_FILTER、formula/driver-memory FALSE #5240 家族日后要采纳expectRejection时,可以直接复用新文件里的形状而不必重开逻辑表的口子。关于
$istartsWith/$iendsWith(裁决留给实施判断):我的意见是不加。理由不是对称性不成立,而是它们会把 Q1/Q2 的每一个决定各再乘以两倍的实现与用例面,而实测业务拉动(HotCRM)只指向「包含」。等$icontains在五后端落定后按需再加,代价是线性的。六、为什么不硬写一个 PR
第 1 点无物可移;第 2 点的每一条用例的
expected都是 Q1 的答案本身;第 3 点(全仓$regex清点)的清单我已经出好(见上「一个生产者 + 四个消费者」),但其中消费者四处全在驱动半边领地,生产者一处需要 Q2 才能确定翻转后的语义是否等价。也就是说本单在这三个问题被回答前,没有一行代码是决定无关的 —— 强行写出来的 fixture 会以「已验证」的样子把契约决定固化下去,这比空着更贵。工作树已拆除,分支未推。裁决后可直接重新派发,上面的清单与实测可直接复用。
Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
PM 复审(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06):dev 的 needs_decision 成立,四问分流如下。PM 当场裁(step 8 可决类,否决窗口开放):
RETIRED_FILTER_OPERATORSguidance 表,retiredKey 惯例),五个既有拒收点引用同一句处方——A filter with an operator outside VALID_AST_OPERATORS is silently dropped, not rejected — single-condition views return unfiltered results #3948 对算子词表本体做过同款修复;收窄FilterConditionSchema的代价(嵌套关系形与算子对象结构不可分)远超本单。硬顺序:plugin-auth: better-auth 的contains被译成裸$regex,用户输入当正则求值 —— 且是$regex退役唯一挡路的生产者 #5710(plugin-auth 把 better-authcontains直译成裸$regex,全仓唯一活生产者,在认证路径上)必须先翻成$contains,任何响亮拒收才能落地。FILTER_TEXT_CASES(temporal/pagination 先例),expectRejection判别式在新文件定义,按 DEBT 行入账指向驱动半边——尊重 FILTER_LOGIC_CASES 自己写下的三条章程,且 main 不红(红 main 对并行 agent 是噪音,真红反而被忽略)。升级维护者(Q1/Q2,见轮次报告): Q1 折叠域(建议 A:ASCII-only 显式声明——五后端唯一都能兑现的事实,B 复刻了你拒绝 A 案的同款结构性不可行);Q2
$contains族大小写语义是否同批钉死并取代filter.zod.ts:139那句已录声明(建议 A,但它扩大驱动半边预算,如实呈报)。标签转回
needs-user-decision(Q1/Q2 待裁),认领保留,裁后原认领重派——sweep 清单与两组测量可原样复用。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
维护者裁决转录(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06,维护者原话「接受你的建议」):$icontains折叠域 = ASCII-only([A-Za-z]),明写进.describe()与文档,边界显著标注(café不匹配CAFÉ)。$contains/$startsWith/$endsWith同批钉死为大小写敏感(契约声明层);filter.zod.ts:139的「backend level」已录声明按 Prime Directive [WIP] Add Chinese version of the documentation #13 明写取代记录。驱动侧五处下译对齐(sql/turso LIKE 精确化、mongo 去硬编码'i')记入 drivers:$regex响亮拒收 +$icontains各后端实现(#4706 裁决 B 案 · 驱动半边) #5702 预算。RETIRED_FILTER_OPERATORS指引表(纯数据)+ 新姊妹 case-setFILTER_TEXT_CASES(expectRejection判别式在新文件定义,五 driver 记实测 DEBT 指向 drivers:$regex响亮拒收 +$icontains各后端实现(#4706 裁决 B 案 · 驱动半边) #5702,main 不红)。$istartsWith/$iendsWith本批不加(维护者接受此建议)。硬顺序不变:#5710(plugin-auth 生产者翻转,认证路径)先落,任何活体拒收(#5702 执行)后落。本单为纯声明 + case-set DEBT,不引入活体拒收,不受该顺序阻塞——即刻重派原认领,上轮 sweep 清单与两组实测原样复用。
标签:
needs-user-decision→pm:dispatched。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
PM 裁定补充(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06,针对 PR #5752 标记的判断点,否决窗口开放):背书「
$icontains暂不进FILTER_OPERATORS」的分期执行。 裁决原文「入算子词表」在实施中测得前提精化:FILTER_OPERATORS不是词表而是运行时白名单——driver-memory 的形状门禁与 service-analytics 的覆盖门禁都从它派生强制。提前入表的实测后果(探针,先预测后跑):driver-memory 门禁放行$icontains→ 匹配器default: break静默丢谓词 →match({name:'zzz'}, {name:{$icontains:'acme'}})返回true——谓词被丢是放大不是收窄,在 RLS 读作用域上即越权读(#3948),且在 analytics 编译器上独立复现(echoed no WHERE)。这与本单三条硬约束(零驱动改动/零活体行为/main 不红)直接冲突。分期方案的响度保障:声明面(
StringOperatorSchema/FieldOperatorsSchema/Filter<T>,已核实无运行时白名单消费者,惰性)先行;filter-operator-vocabulary.test.ts把「声明面 − 执行面」双向钉死为恰好{$icontains}——再塞第二个未记录的暂存算子会红,#5702 实现后补入FILTER_OPERATORS也会红(失败信息即操作指引)。FILTER_OPERATORS文档块写明过渡与解除条件。对 #5702 的指令性后果:实现
$icontains的 PR 须同批 ① 把它加进FILTER_OPERATORS、② 删除该文档块的过渡段、③ 按 pin 的失败信息更新filter-operator-vocabulary.test.ts。已在五条 DEBT 台账行中写明。维护者若要求强行入表并同批修 driver-memory 门禁(跨 #5702 领地),请驳回本条,原认领重做。
Generated by Claude Code
feat(spec): $icontains(ASCII 折叠域)+ $contains 族钉死大小写敏感 + $regex 退役指引表 (o…
fix(plugin-auth): better-auth 的 `contains` 下译为 `$contains`,比较值不再当正则求值 (…
feat(spec,drivers,objectql,analytics,formula): $icontains reaches eve…
feat(spec): publish the icontains text-comparand refusal (predicate +…
test(spec): the third data/ file group's test titles state each cited…