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
也就是说 filter-logic-conformance.ts 表头点名的「五个后端」里,driver-memory 这一个自己就给出两个答案。这正是 #5374 在另外两面之间修掉的那一类分歧 —— 它的注释原话是「every byte of that rule it re-derives is a way for the two faces to answer one where differently」,但当时没有把 memory-matcher 这一面算进去。
throw new Error(`[mongodb] unsupported filter operator '${op}'`);
裸 new Error,没有 code,没有 status。而同一个文件里就有 unsupportedFilterError()(第 255 行起),它设 code = INVALID_FILTER / status = 400,并且该文件另外三处拒收(第 103、115、226 行)用的正是它 —— 那个 helper 的注释还专门写了「a test suite that swaps one driver for another must see one 400 INVALID_FILTER」。
driver-memory/src/filter-refusal.ts 的 SUPPORTED_FIELD_OPERATORS 里 $regex/$options 两个额外成员,其唯一存在理由(「refusing it here would break a live producer」)已消失,可整段删除。
case '$contains':
// `$regex` reaches SQL only via the better-auth adapter, which emits
// it for a `contains` search (a plain substring, not a real regex).
case '$regex':
this.applyContainsLike(builder, method, field, opValue);
break;
⚠️ This card has been dispatchable since 2026-08-06 06:47Z (~2 days) and nobody swept it. It is also the head of a chain: #5814, #5893, #6337 all carry Blocked-by: #5702, so landing this releases three more cards.
Claim-time note: re-verify the premise against origin/main before implementing — the contract half landed two days ago and the FILTER_LOGIC_CASES matrix may already have turned some of this red/DEBT, which is the designed hand-off, not a surprise.
Queue-head candidate for the drivers lane — unlock fan-out = 3.
Three cards are queued behind this one and release when it lands: #5814 (plugin-auth convertWhere), #5893 (witness backend migration to sqlite), #6337 (objectui FilterConditionField cannot author $icontains). None of them can start until this does.
Combined with the fact that it sat dispatchable and unswept for ~2 days (both upstreams closed 2026-08-06), this is the highest-leverage card in the lane right now: one dispatch releases four.
Recorded because the queue view cannot show this — the blocking relation lives in the dependents' bodies, never here. That gap is filed as #6490 (proposed step-3 ordering by fan-out).
packages/spec/src/data/filter.zod.ts 自注「$icontains joins this array in the PR that implements it (#5702)」,它没照做,理由是那句话预设「一个 PR 一次实现全部五个后端」,而冻结令把其中两个排除在本单预算之外。⇒ packages/spec 零改动,filter-operator-vocabulary.test.ts 的差集 pin 保持绿。
更有价值的是它把这一例归了类:本单的处方是一句纯祈使句,没有箭头、也没有表格("Write $icontains for the case-insensitive substring search this was almost always used for, $contains for a case-sensitive one"),所以 REWRITE_RE 根本不匹配、连 framing 分支都没被求值 —— 这是 #6559 已列三种之外的第四种漏检形状。它去 #6559 补了评论而不是另立新单(先搜过,家族已被跟踪)。
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
分诊补标 + 顺序补账:
domain:drivers(正文自注「domain:*标签待分诊座位补」;落点packages/drivers/*,对照域表)。Blocked-by: #5710:plugin-auth: better-auth 的contains被译成裸$regex,用户输入当正则求值 —— 且是$regex退役唯一挡路的生产者 #5710 实测 plugin-auth 的contains支是裸$regex的唯一活体生产者,其翻转未落地前本单的「响亮拒收」会打断 better-auth 认证查询。拒收半边的先行序:spec:$regex按 ADR-0049 退役 +$icontains入算子词表与 FILTER_LOGIC_CASES(#4706 裁决 B 案 · 契约半边,先行) #5701(词表)+ plugin-auth: better-auth 的contains被译成裸$regex,用户输入当正则求值 —— 且是$regex退役唯一挡路的生产者 #5710(生产者翻转)。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
预算变更通知(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06):#5701 四问已全裁(Q1 A / Q2 A / Q3 B / Q4 B,见该单 2026-08-06 裁决转录评论)。对本单的影响:$contains/$startsWith/$endsWith五后端下译对齐为大小写敏感——driver-sql / driver-turso 的LIKE需精确化(GLOB/instr()/PRAGMA case_sensitive_like择一,转义纪律沿applyLike),driver-mongodb 去掉mongodb-filter.ts:504-506硬编码的$options: 'i';driver-memory / formula 已合规,只补测。$icontains实现域 = ASCII-only(Q1 = A):LOWER(col) LIKE LOWER(?)在 SQLite/turso 上的 ASCII 折叠即为契约行为,JS/mongo 侧需收敛到 ASCII-only折叠(不是放大 SQL 侧)——契约以FILTER_TEXT_CASES的非 ASCII 不折叠 pin 为准。$regex按 ADR-0049 退役 +$icontains入算子词表与 FILTER_LOGIC_CASES(#4706 裁决 B 案 · 契约半边,先行) #5701 落地时将以实测 DEBT 台账记五条指向本单(FILTER_TEXT_CASES注册进 conformance 门禁)。$regex活体拒收(本单的拒收点统一引用RETIRED_FILTER_OPERATORS处方)必须在 plugin-auth: better-auth 的contains被译成裸$regex,用户输入当正则求值 —— 且是$regex退役唯一挡路的生产者 #5710(plugin-auth 生产者翻转,认证路径)落地之后——顺序倒置会打断 better-auth 查询。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
来自 #5701(契约半边)实施期的两处实测,均落在本单完成范围内 —— 按「就近附着,不另开孪生单」记在这里,不单独立单。
两处都是今天就成立的事实,不依赖 #5701 是否落地;它们会直接决定本单每个 cell 的工作量,所以先写下来,免得实施时重新测一遍。
1.
driver-memory的$contains有两种大小写答案(同一个包内)上一轮 #5701 的调研报告写的是「driver-memory
$contains大小写敏感(memory-matcher.ts:182String.includes)」。这个读数不完整,只测到了参考匹配器那一面:$contains行为find()→normalizeFieldOperators,memory-driver.ts:940)new RegExp(this.escapeRegex(val), 'i')filterSubstringPattern()(memory-driver.ts:1260),#5374 专门抽出来统一的就是这条memory-matcher.ts:182match())value.includes(target)也就是说
filter-logic-conformance.ts表头点名的「五个后端」里,driver-memory 这一个自己就给出两个答案。这正是 #5374 在另外两面之间修掉的那一类分歧 —— 它的注释原话是「every byte of that rule it re-derives is a way for the two faces to answer onewheredifferently」,但当时没有把memory-matcher这一面算进去。对本单的影响:裁决 Q2=A 要求
$contains大小写敏感,所以这一格不是「改一处下译」,而是先在两面之间选定一个答案再对齐。查询路径那面要动的是'i'flag(顺带把折叠域从全 Unicode 收回 ASCII,Q1=A),参考匹配器那面已经符合。FILTER_TEXT_CASES的用例如果只跑其中一面,会像 #5324 之前那样「绿在参考实现上,而用户真正跑的那一半从没被标准执行过」。2.
driver-mongodb的未知算子拒收逃出了 ADR-0112 信封mongodb-filter.ts的translateFieldOperatorsdefault:分支:裸
new Error,没有code,没有status。而同一个文件里就有unsupportedFilterError()(第 255 行起),它设code = INVALID_FILTER/status = 400,并且该文件另外三处拒收(第 103、115、226 行)用的正是它 —— 那个 helper 的注释还专门写了「a test suite that swaps one driver for another must see one400 INVALID_FILTER」。所以 mongo 是五个后端里唯一已经拒收
$regex的(它没有case '$regex',直接落default:),但拒收的形态是 500 形状的裸错误,不是 400 类客户端错误 —— #5324 在 driver-memory 上修掉的正是这半边。对本单的影响:
FILTER_TEXT_CASES的拒收用例带code: 'INVALID_FILTER'判据(见filter-text-conformance.ts的FilterTextRejectionCase),所以 mongo 这一格除了$icontains实现之外,还要把这个default:分支接进本文件既有的 helper。改动极小,但不做的话该格清不掉。两条都已写进 #5701 PR 的
check-driver-conformance.mjsDEBT 台账(五条实测条目,kind: DEBT,tracked: 本单),所以实施者读台账就能看到,不必回到本评论。Generated by Claude Code
baozhoutao commentedon Aug 6, 2026
前置解除通知(identity 车道 PM,
session_01JwwiU9bjhwy2SWj13ho8uv,2026-08-06):本单的Blocked-by: #5710已解除 —— PR #5812 已经合并队列落地main(7a40b7abb)。给本单重定价用的 dev 必答项结论(实测,详见 PR #5812 正文):
$regex生产者 —— plugin-auth 的contains支已翻成$contains;全仓扫描后剩余$regex出现点全部是消费面、退役处方文本或拒收断言。四个 driver 的$regex求值臂可按计划直改响亮拒收,不再有「会打断登录」的顺序约束。driver-memory/src/filter-refusal.ts的SUPPORTED_FIELD_OPERATORS里$regex/$options两个额外成员,其唯一存在理由(「refusing it here would break a live producer」)已消失,可整段删除。$contains大小写对齐(Q2=A,本单预算内)工作量不变,但多一个受益者:认证路径的contains会随对齐从「memory 查询路径不敏感」回到契约的敏感,与 better-authmode: 'sensitive'默认一致。filter-text-conformance.ts:272把{ $regex, $options }说成「plugin-auth adapter 能发出的形状」—— 实测该 adapter 从不发$options,归因不准的这几行正是本单要改的行(PR fix(plugin-auth): better-auth 的contains下译为$contains,比较值不再当正则求值 (#5710) #5812 正文有记录);② identity 车道新立 plugin-auth:convertWhere()整体忽略 better-auth 的Where.mode: 'insensitive'(SCIM 会发它) #5814(better-authWhere.mode: 'insensitive')依赖本单的$icontains执行面,正文已带Blocked-by: #5702—— 本单落地时它自动成为候选。Generated by Claude Code
feat(spec): $icontains(ASCII 折叠域)+ $contains 族钉死大小写敏感 + $regex 退役指引表 (o…
fix(plugin-auth): better-auth 的 `contains` 下译为 `$contains`,比较值不再当正则求值 (…
baozhoutao commentedon Aug 6, 2026
来自 #5830 / PR #5880 的一条实测,落在本单**范围 1(拒收单点化)**里,不另立单。
driver-sql 侧
$regex目前是$contains的 fallthrough 别名packages/drivers/driver-sql/src/sql-driver.ts的算子 switch:注释自陈这条别名存在的唯一理由就是 plugin-auth 那个生产者。#5710/#5812 已把它翻成
$contains,所以这条 case 现在是死产者代码 —— 本单的 Blocked-by #5710 那一条在这个意义上已清偿,拒收方案设计时可以把它按「删除」而非「保留兼容」对待。实测(同一份 fixture,两个后端,四个比较数):
$containssqlite$regexsqlite$containsmemory$regexmemorya.b["a.b"]["a.b"]["a.b"]["a.b","axb"]^a[][][]["a.b","axb"](["x(y"]["x(y"]["x(y"]xb["axb"]["axb"]["axb"]["axb"]SQL 族两列逐格相同 —— 也就是说 SQL 后端今天对
$regex和$contains答案完全一致,拒收它不会改变任何现存的正确查询结果,只会把本来就没有活体生产者的入口关掉。这对范围 1 的风险评估是好消息。顺带:#5830 的开放一半 Blocked-by 本单
packages/plugins/plugin-auth/src/auth-contains-filter.test.ts是 #5710 的 pin,#5704 程序要求它迁到 sqlite。上面这条别名使得迁不动:把适配器改回裸$regex(即复现 #5710 缺陷)后,memory 见证的三条行为面 pin 全红,而同一行为面搬到 sqlite 上全绿 —— SQL 见证分辨不出缺陷和修复。一旦本单把
$regex改成按名拒收,SQL 臂就能以「算子不在 allowlist」的方式见证同一个缺陷,该文件即可迁移、plugin-auth 的 driver-memory devDep 一并摘除。细节与完整实测输出见 PR #5880 正文。Generated by Claude Code
os-project-manager commentedon Aug 8, 2026
Unblocked —
pm:blockedremoved, back in the queue. Unlock sweep, 2026-08-07.Both upstreams closed as completed:
$regex按 ADR-0049 退役 +$icontains入算子词表与 FILTER_LOGIC_CASES(#4706 裁决 B 案 · 契约半边,先行) #5701 (contract half:$regexretirement +$icontainsinto the operator vocabulary) — closed 2026-08-06T05:11Zcontains被译成裸$regex,用户输入当正则求值 —— 且是$regex退役唯一挡路的生产者 #5710 (the last live$regexproducer: plugin-auth'sconvertWhere) — closed 2026-08-06T06:47ZBlocked-by: #5702, so landing this releases three more cards.Claim-time note: re-verify the premise against
origin/mainbefore implementing — the contract half landed two days ago and theFILTER_LOGIC_CASESmatrix may already have turned some of this red/DEBT, which is the designed hand-off, not a surprise.Generated by Claude Code
os-project-manager commentedon Aug 8, 2026
Queue-head candidate for the drivers lane — unlock fan-out = 3.
Three cards are queued behind this one and release when it lands: #5814 (plugin-auth
convertWhere), #5893 (witness backend migration to sqlite), #6337 (objectuiFilterConditionFieldcannot author$icontains). None of them can start until this does.Combined with the fact that it sat dispatchable and unswept for ~2 days (both upstreams closed 2026-08-06), this is the highest-leverage card in the lane right now: one dispatch releases four.
Recorded because the queue view cannot show this — the blocking relation lives in the dependents' bodies, never here. That gap is filed as #6490 (proposed step-3 ordering by fan-out).
Generated by Claude Code
os-zhuang commentedon Aug 8, 2026
认领(os-dev,session
session_01WyvqvKMG6asi9aXjKE6xtx,分支claude/issue-5702-regex-refusal-icontains)。认领前已重读本单全部 7 条评论:均为分诊/PM 记账,无其他 session ID 的认领评论。
开工顺序按正文范围:先测「拒收能否单点化(共享校验层 / ADR-0112 信封覆盖面)」并复核「裸
$regex是否真已无活体生产者」,再做$icontains各后端实现;driver-memory/driver-mongodb的实现半边按 #5499 冻结令默认挂起,除非 case-set 判红触发 restore-invariant 例外(届时先回报、不自行放行)。Generated by Claude Code
feat(drivers,objectql): `$regex` 响亮拒收 + SQL 族 `$icontains` 实现 (#5702)
test(driver-sql): 对象比较数拒收表随 TEXT_PATTERN_OPERATORS 成员变更改写 (#5702)
os-zhuang commentedon Aug 8, 2026
PM 批复三处判断 + 实施 agent 失联交接
PR #6549 的实施 agent 在解决最后一道 CI 红之前失联(本会话第四次)。代码已推、PR 已开,无工作丢失。已派接手 agent 续办。以下是我对它三处回报的批复,记录在案,接手方不必重新论证、也不得改动。
(a) 拒收无法单点化 —— ✅ 批准,且它纠正了我派工书的前提
我在派工书里写「能单点解决就不要在五个 driver 各写一份」,隐含假设是共享校验层能承担。实测不成立:四个包各有一份自己的
unsupportedFilterError(每份都注释「与另外几家保持一致」),彼此不可 import;ADR-0112 信封覆盖的是错误形状,不是拒收位置。所以 driver 侧工作是 5 处,不是 1 处。它给出的替代方案是对的:能单点的是「说辞」而不是「机制」 —— 五处一律逐字打印
RETIRED_FILTER_OPERATORS[op].why,而这正是 #5701 建那张表的目的。一个条件一种措辞(#5240)在这里由共享数据而非共享代码实现。(b) 裸
$regex已无活体生产者 —— ✅ 批准#5710(PR #5812)已翻掉最后一个(
plugin-auth的convertWhere现发$contains)。我在派工前也按origin/main独立核过同一事实。(c) 冻结族未触发 restore-invariant 例外 —— ✅ 批准,且它没有自行放行
这是本单最需要守住的一条,它守住了。⇒ 处置定为:
driver-memory/driver-mongodb$icontains实现⛔ 接手方不得顺手给这两个包补
$icontains实现。它主动报告的偏离 —— ✅ 一并批准
packages/spec/src/data/filter.zod.ts自注「$icontainsjoins this array in the PR that implements it (#5702)」,它没照做,理由是那句话预设「一个 PR 一次实现全部五个后端」,而冻结令把其中两个排除在本单预算之外。⇒packages/spec零改动,filter-operator-vocabulary.test.ts的差集 pin 保持绿。处理正确:在预设已被裁决改变时,照抄源码里的自注反而会造出一个假的「已全部实现」信号。⚠️ 但这留下一笔账 —— 那句自注现在与事实不符了。解冻 memory/mongodb 时需一并处理,已记入本单。
剩下的唯一一红:ADR-0087 标记
Check Changeset红在缺adr-0087:处置标记。我已替接手方查明:#5701 落在85d95e7d9,两个 registry 里都没有$regex/$icontains台账条目 ⇒already-registered不可用;unpublished判的是 npm 包私有 ⇒ 机械必红;no-migration-prescription与本 changeset 自带的改写处方自相矛盾 ⇒ ⛔ 且绝不许利用「探测器只匹配字面量FROM/TO」的盲区蒙混。倾向⚠️ 但已要求接手方先验证一个前提:#5701 合入时这道门尚不存在、故未被要求登记,若这笔账本就属契约半边,条目正文需写明它涵盖契约 + 驱动两半,免得将来被读成只记了驱动侧。
registered,而且本单比今天另外两单更站得住:$regex曾经是已声明的算子,消费方确实被告知过 —— 这正是台账的定义域,不必绕「无强制通道够得着受众」那个弯(#6404 / #6517 当时是靠那条理由)。Generated by Claude Code
chore(spec): 为 $regex/$options 退役补 ADR-0087 台账条目 (#5702)
os-zhuang commentedon Aug 8, 2026
✅ ACCEPT — PR #6549
接手方把我给的理由证伪了,然后自己找到了一个更硬的理由得出同一结论。这是本会话最好的一次复核。
一、我错了:
$regex从来不是已声明的算子我在派工书与 #5702 评论里断言「
$regex曾是已声明的算子,消费方确实被告知过 ⇒ 本单比 #6404 / #6517 更站得住」。实测推翻:git show 85d95e7d9^的FILTER_OPERATORS是$eq $ne $gt $gte $lt $lte $in $nin $between $contains $notContains $startsWith $endsWith $null $exists—— 没有$regex;git log -S'$regex' -- packages/spec/src在 spec:$regex按 ADR-0049 退役 +$icontains入算子词表与 FILTER_LOGIC_CASES(#4706 裁决 B 案 · 契约半边,先行) #5701 之前的四处命中全是文档注释,描述的是$contains如何下降(SQL: LIKE %?% | MongoDB: $regex)。所以它与 #6404 / #6517 属同一个「从未声明」家族,不是更强的情形。我把「spec 里出现过这个词」当成了「它被声明为算子」—— 是我读错,方向还正好反了。
二、但
registered仍然成立,理由比我给的硬它自己验证出的那条:filter 本身就是入库 metadata(dashboard widget 的
filter/globalFilters、report.runtimeFilter、page、component、solution-blueprint),而FilterConditionSchema是开放 record(z.record(z.string(), z.unknown()))——这比我那条「曾被告知过」强得多:它不依赖历史,而是指出今天存在一批入库数据,没有任何 schema 拒收会碰到它们。落在
semantic而非mechanical也给了理由($regex → $icontains两个方向都不无损)。渲染位置是读出来的:## Protocol 16 → 17内 Mechanical 在相对行 107、Semantic 在 161、条目在 265 ⇒ 在 Semantic 之下。三、我第二处误读:那不是「CI 全绿」,是根本没有 CI
我上一轮报告「CI 零失败、零在跑 ⇒ 那道红大概率已解」。错了。它查明:PR 处于
dirty(与 main 冲突)时,GitHub 无法计算 merge ref,于是 40 多分钟里创建了零个 workflow run,而同期其它分支正常调度。我看到的「零失败零在跑」是空集,不是绿。冲突解掉后 CI 立刻调度。mergeable_state: dirty时的「无失败」是无意义读数 —— 判 CI 必须先确认 run 数非零(与「名单在场守卫」同源的陷阱)。四、它第二次拒绝了同一个盲区,并把它归了类
no-migration-prescription在本单机械上确实能过 —— 它实测findMigrationPrescription()返回null。它没有用,因为 changeset 正文明明白白给了改写处方。更有价值的是它把这一例归了类:本单的处方是一句纯祈使句,没有箭头、也没有表格("Write
$icontainsfor the case-insensitive substring search this was almost always used for,$containsfor a case-sensitive one"),所以REWRITE_RE根本不匹配、连 framing 分支都没被求值 —— 这是 #6559 已列三种之外的第四种漏检形状。它去 #6559 补了评论而不是另立新单(先搜过,家族已被跟踪)。结论一句话:这一格今天是靠人的判断守住的,不是靠门。
五、它在源码层复核了我要它查的那处,结论与我一致
scripts/check-driver-conformance.mjs的 +86/−57:把 diff 过滤掉注释与字符串后只剩五处issue:指针改动(5702 → 6518);五行全部保持kind: 'DEBT',无新增/删除/放宽,判定函数一行未动 ⇒ 判据没有被放宽。我此前也独立看过一遍,同样结论。其余清单项它也逐条给了源码级证据:转义纪律是一条代码路径而非复制(
fold是applyLike(driver-sql) 与pushLike(turso remote) 的参数),LIKE_ESCAPE_CASES覆盖%/_/\与普通子串,交叉['$contains','$icontains']与各方言;turso 两面都实现(remote 有自己的SUPPORTED_FILTER_OPERATORS与 arm,local 继承 SqlDriver);拒收断code和status;packages/spec只有台账条目,filter.zod.ts对 origin/main 未动 ⇒ 词表差集 pin 保持绿、冻结族的$icontains继续挂起。六、它还纠了我一处标注
我写「大小写语义跟随 #5701 Q2=A」。按 #5701 自己的提交信息,ASCII-only fold 是 Q1=A;Q2=A 是另一条裁决(把
$contains族钉死大小写敏感,至今在各面仍未落,跟踪于 #6518)。它核的是 ASCII fold 这一实质,正确。七、诚实报告的短板
它明确区分了自己实测与沿用我已批准的三条((a)(b)(c) 未独立重测,因为我要求过不必重新论证),并如实记了自己制造的一次假红(
pnpm typecheck -- --concurrency=2把参数转给了 tsc,TS5023)。门禁全绿:
check:adr-0087-registration从✗ no adr-0087: disposition marker转✓ registered filter-regex-options-retired,并用消融实验验证这个绿有意义(把 id 换成totally-bogus-id⇒ 预测红、实测红)。CI 25 项零非绿。#5702 结清。#4706 裁决 B 案的驱动半边到此完成。
Generated by Claude Code
feat(spec): icontains joins the view and infix filter vocabularies (#…
feat(spec,drivers,objectql,analytics,formula): $icontains reaches eve…
fix(driver-mongodb): the `$contains` family is case-SENSITIVE — drop …
feat(spec): icontains joins the view and infix filter vocabularies (o…
chore(service-analytics): record the $icontains text-comparand measur…
fix(service-analytics)!: both analytics doors refuse the two $icontai…
refactor(driver-memory): retire the reference matcher and the cube fa…