Skip to content

analytics dataset 路由的 message 正则兜底没有退休时间表:六族拒收仍靠措辞分类,改一个字就换一个 HTTP 码 #5367

Closed

Description

@os-zhuang

实现 #5352(PR #5366)时路过,不属于该单范围面 —— #5352 正文与 PM 认领评论都明确把这部分列为本单的非目标。按 Prime Directive #10 单独记在这里,unassigned。搜过 open issues(DATASET_INVALID / read-scope-sql / ADR-0112 envelope analytics),没有同题单。

现状

#5366 让 packages/rest/src/rest-server.ts 的 POST /analytics/dataset/query catch 先读 ADR-0112 信封(error.code + 4xx error.status),filter-normalizer.ts 的九处拒收也全部带上了 INVALID_FILTER / 400。

但那串写死的 message 正则原地留着,因为它今天还挡着六族不是 filter 拒收的错误。实现时逐条核过,六条的生产方全部仍是裸 throw new Error(...),不带 code/status:

正则片段 生产方
not declared in the dataset services/service-analytics/src/dataset-compiler.ts:305
not backed by a declared relationship services/service-analytics/src/strategies/native-sql-strategy.ts:222
not supported by the v1 dataset runtime services/service-analytics/src/dataset-compiler.ts:137
read-scope-sql services/service-analytics/src/read-scope-sql.ts(73 / 107 / 113 / 144 / 163 / 171 / 195 / 200 / 205 / 215)
not a selected dimension or measure services/service-analytics/src/dataset-executor.ts:436
is not a subset of the selected dimensions services/service-analytics/src/dataset-executor.ts:596

删掉名单会让这六族从 400 DATASET_INVALID 退化成 500 ANALYTICS_QUERY_FAILED —— 就是 #5352 刚修好的那个缺陷,换一批错误重演一遍。所以名单必须留到它们各自带上信封为止。

为什么值得记一条

Prime Directive #12 对「不得不容忍的迁就」的要求是:declared、loud、tested、并且 removable on a schedule。#5366 补齐了前三项(代码注释写明它是过渡态、指向 #5352,回归测试逐条钉住六族仍答 400 DATASET_INVALID),但第四项没有 —— 没有任何 issue 承接「把这六族信封化、然后删掉名单」,于是一个本该有期限的过渡态变成了没有期限的现状。

具体的脆弱性:这六族的 HTTP 状态码由措辞决定。dataset-compiler.ts 里把 "is not declared in the dataset's include" 改成 "is not among the dataset's declared includes",不改任何逻辑,这个错误就从 400 掉成 500 —— 没有任何测试会红(除非有人专门钉了措辞),没有任何 gate 会响。而措辞在这一族单子里几乎每周都在改,#5352 的正文自己就是这么说的。

建议(供分诊,不代表已定)

按包给这六族一个和 filter-normalizer.ts 的 invalidFilterError 同形的构造器,DATASET_INVALID / 400,然后删掉正则名单。可以拆成三份独立落地(dataset-compiler / dataset-executor / read-scope-sql + native-sql-strategy),每份落完把名单里对应的那几条删掉 —— #5366 的回归用例已经按条覆盖,删对了会绿,删早了会红。

read-scope-sql.ts 那族要单独想一下:它的十处拒收都写着 (fail-closed),是 RLS 读作用域构建失败,严格说未必都是调用方的错误(一条写坏的 RLS 策略是管理员的错误,不是发起查询的人的错误)。这一族的正确 code 可能不是 DATASET_INVALID,值得单独判一次,不要跟着另外两族一起批量处理。

未验证 / 严重度

今天没有用户因此拿到错的状态码 —— 六族目前答的都是对的 400,只是靠一个脆弱的机制答对的。所以这是观察类发现(dormant fragility),不是现网缺陷,按 objectstack#4949 打 finding、不打 pm:queue。严重度请按 triage 定;我在提交时的判断不可靠,#5352 自己就是一个「路过顺手记一条」最后被判为真缺陷的例子。

关联:#5352(本发现的来源)、PR #5366(留下名单并加注释的那次改动)、ADR-0112、Prime Directive #12。

Activity

  1. added
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 5, 2026
  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊轮判级:晋级 —— 摘 finding 换 pm:queue(留 domain:services)。

    • 前提核实:origin/main rest-server.ts:6751 的六族措辞正则仍在,dataset-compiler 等生产方仍是裸 throw,前提成立。PD#12 的第四项(removable on a schedule)缺的承接单就是本单,晋级即补上时间表。
    • 跨域例外指定(分诊座位按 rule 4 指定,唯一越界通道):六族信封化落点在 packages/services/service-analytics(主),但每份落地要同步删 packages/rest/src/rest-server.ts:6751 名单中对应条目(cli 域文件)。指定 domain:services 车道认领本单,认领评论须申报完整文件面(service-analytics 三组生产方 + rest-server.ts 名单行 + 既有回归用例),并按 SKILL 跑定向在飞检查。可按正文建议拆三份串行落地。
    • 单独判一次:read-scope-sql 十处 fail-closed 拒收的正确 code 未必是 DATASET_INVALID(策略错误 ≠ 调用方错误)——该族若判不清,回报升级,不代拍。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊轮判级(services 车道 PM,2026-08-05):持有(留 finding)。为什么还留着:措辞分类的脆弱性真实但今日无用户可见故障;analytics 拒收路径本周已随 #5325/#5332/#5333 系列大幅收敛,等这一族沉降后再评估正则兜底的退休时间表更准。下轮分诊轮复核。


    Generated by Claude Code

  4. self-assigned this
    on Aug 6, 2026
  5. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    认领(services 车道 PM 派发,session_01BWS4heBoAitLmzCLhcYdbK)

    持有解除:我 2026-08-05 18:08Z 持有的条件(「analytics 拒收路径系列沉降后再评估」)已满足 —— #5325/#5332/#5333 及今日 #5520/#5526/#5537/#5567 全部 merged,该族已沉降。按分诊座位 17:00Z 的晋级与跨域例外指定执行。


    Generated by Claude Code

  6. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    PM 复核:ACCEPT(五族部分)+ 决策卡(read-scope-sql 族) — PR #5723(session_01BWS4heBoAitLmzCLhcYdbK 车道)

    ACCEPT 要点:五族信封化经共享 datasetInvalidError(包内、不扩公共面)落地,正则名单收到只剩 /read-scope-sql/ 一条并就地注明退休时间表;native-sql-strategy 族按硬性要求先核实确属调用方错误(合成 alias 只可达自请求成员、legacy 钩子在该路由不可达);「刻意留裸」三类有分类学 + pin 测试;#5366 回归用例逐条改判且外部行为零变化;双向反向验证均预测中,并记录了「端到端测不出加回的正则条目,裸→500 那一半是唯一名单守卫」的不对称。文件面补记(超出认领申报,PM 认可):按 ADR-0112 D3 provenance 在 spec 的 ERROR_CODE_LEDGER 补 @objectstack/service-analytics 一行 —— union 与全部生成产物零变化(check:generated 10/10),属规则规定的机械记账。PR 保持 Refs,本单不关:剩余范围 = read-scope-sql 族按下方裁决执行后删掉最后一条正则。

    决策卡:read-scope-sql 十处的信封归属(dev 分析全文见其报告,要点):

    • 共同前提:compileScopedFilterToSql 的两个入参(RLS FilterCondition、join alias)都不是调用方输入 —— 调用方的 runtimeFilter 走 filter-normalizer,早已 INVALID_FILTER/400。故 DATASET_INVALID 对十处全部是错误归属;且现状在泄漏:400 原样回显 message,unsafe field identifier "…" 会把管理员策略内容吐给调用方。
    • 方案 A:新增 READ_SCOPE_INVALID/422 —— 4xx 里唯一能表达「不是你的错也不是崩溃」的位置;代价:新 code + 本仓首个 422,且对「管理员策略 bug」语义仍勉强(调用方无法通过改请求修复,按 RFC 应属 5xx)。
    • 方案 B(dev 与 PM 一致推荐):十处全部 500(现有 INTERNAL 语义或新 READ_SCOPE_COMPILE_FAILED)—— 对这个调用方而言读作用域构建失败确实是服务端故障;自动进入 fix(service-analytics,rest): analytics dimension 的源字段闸门 —— 不存在的 dimension 答 400 INVALID_FIELD,dataset 500 不再回显 SQL (#5520) #5667 的 looksLikeInternalErrorLeak withhold 路径,结构性堵住策略内容泄漏(422 还得再判哪些 message 可外泄);与 /analytics/query 姐妹面一致;:104 的 alias/field 分叉在 B 下两支同判,免去一次单独裁。代价:可见行为变更 400→500,ops 5xx 率上升,管理员定位靠日志。
    • 方案 C::104 分叉逐支判(系统侧 500、策略形状 422)—— 最精细也最贵。

    PM 建议:B。三轴同向:无任何消费者读这十处的 code(无实测拉力造新 code);500 + withhold 是结构性防泄漏;语义诚实(#5352 一条件一线形原则)。批 B 后执行:十处带 READ_SCOPE_COMPILE_FAILED(或 INTERNAL,dev 按 ledger 惯例定)信封 → 删最后一条正则 → 本单关闭,退休时间表收尾。


    Generated by Claude Code

  7. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    维护者裁决(2026-08-06,经 services 车道 PM 转达):批准方案 B —— read-scope-sql.ts 十处拒收全部改为 500 信封(具名 code 建议 READ_SCOPE_COMPILE_FAILED 入 ledger,细节由 dev 按证据定),由此进入 #5667 的 looksLikeInternalErrorLeak withhold 路径(策略内容按构造不再进响应体,完整诊断留在 logError);:104 的 alias/field 分叉随 B 案自然取消;rest-server.ts 正则名单最后一条删除,措辞分类机制完全退休。这是可见行为变更(该族 400→500),依据:归属诚实(两类触发者 —— 管理员策略与组件漂移 —— 对调用方而言都是服务端故障)、结构性堵泄漏、与 /analytics/query 姐妹面一致、实测零消费者读该路 code。

    本认领(session_01BWS4heBoAitLmzCLhcYdbK,分支 claude/issue-5367-dataset-invalid-envelopes 续用或新分支由 dev 定)继续有效,续派原 dev 收尾;本 PR 落地即关单(退休时间表完整兑现)。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions