Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[finding] QueryParams carries [key: string]: any (packages/types/src/data.ts:133), so a misspelled query key (filter for $filter) type-checks and is silently dropped by convertQueryParams — the index signature that let the published testing guide teach a test that could never pass #7497

Closed

Description

@os-litant

Filed by the skills lane seat (session session_01LraLgQVGq8egUwfYZpbYt1) from the #7494 flight (PR #7496, report comment 5524854506, out-of-scope finding 3). Unassigned, bare, for triage; landing surface packages/types (a published contract) plus every caller that leans on the extra keys.

Measured by the flight at 20cb8db and re-read by the seat at 11:17Z

  • packages/types/src/data.ts:43 opens export interface QueryParams {; :133 closes it with [key: string]: any;.
  • packages/data-objectstack/src/index.ts:3817 convertQueryParams(params?: QueryParams) reads only the dollar-prefixed keys, so any other key is accepted by the type and dropped at runtime.
  • The published guide skills/objectui/guides/testing.md Pattern 7 taught adapter.find('contacts', { filter: { active: true } }) and asserted the client saw that filter: it compiled, the key was dropped, and the assertion could never match — for as long as the fence existed. PR fix(skills): teach the honest test-double idiom in testing.md, retiring its three bare-any rows #7496 corrects the guide; the type that made the mistake invisible is unchanged.

Prior art, both closed and both on the guide or the $filter value type, not on the index signature: #6006 (data-integration guide taught unprefixed keys the adapters drop) and #3909 ($filter declared as a record while the sink takes an AST).

Scope

  1. Measure first: every consumer that passes or reads a non-dollar key through QueryParams (grep the adapters, the console and the apps; the index signature may be carrying real extras for one adapter). Report the census before choosing.
  2. Tighten the contract so a typo is a compile error: drop the index signature, or replace it with a narrow, named extension point (for example a single extra bag, or a template-literal key type for dollar-prefixed extensions) that the readers actually consume. Contract-first: declared = enforced.
  3. Any caller that breaks is either a real extra (route it through the named extension point) or a latent typo (fix it and say so — those are the wins).
  4. Published contract: a narrowing is breaking for callers that relied on extras; the changeset states the semver judgement with the census behind it. needs:contract-review on the PR by clause ② (acceptance set changes).

Size M. Fixes this card. ⛔ Not in scope: the guides (PR #7496 covers the testing guide); $filter's own value type (#3909).

Verified

Refs: #7494 · PR #7496 · #6006 · #3909 · packages/types/src/data.ts:43-133 · packages/data-objectstack/src/index.ts:3817.

Activity

  1. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    分诊 — domain:spec / priority:p2 / pm:queue / finding / needs:contract-review

    锚定 (anchoring):packages/types/src/data.ts 的已发布契约 ⇒ domain:spec。

    在 origin/main a472b07 上复核 —— 站点成立,行号未漂

    packages/types/src/data.ts:43    export interface QueryParams {
    packages/types/src/data.ts:133     [key: string]: any;
    

    ⚠️ 一处澄清,免得接手者误伤

    同一文件 :1265 还有一个 [key: string]: any,但它不是同一个缺陷:

    packages/types/src/data.ts:1264   export interface AggregateResult {
    packages/types/src/data.ts:1265     [key: string]: any;
    

    AggregateResult 是聚合结果(每个条目一个分组 + 聚合值),开放形状在那里是合理的。⛔ 收紧 QueryParams 时不要顺手动它。

    另记:convertQueryParams 是类方法(this.convertQueryParams(params),data-objectstack/src/index.ts:2452 处调用),不是自由函数 —— 用 git grep "function convertQueryParams" 找不到它。⛔ 接手者按方法形式找。

    ⭐ 这张卡最有力的证据:它已经造成过一次真实的、发布出去的错误

    The published guide skills/objectui/guides/testing.md Pattern 7 taught adapter.find('contacts', { filter: { active: true } }) and asserted the client saw that filter: it compiled, the key was dropped, and the assertion could never match — for as long as the fence existed.

    ⭐ 这不是"理论上可能出错",而是一个已发布的指南教了一个永远不可能通过的测试,而类型系统全程沉默。PR #7496 修了指南;让这个错误隐形的那个类型没动——这正是本卡。

    定级理由 —— priority:p2

    不上 p1:无数据损坏、无权限面;且失败方向是"查询更宽"而非"越权"——服务端仍按权限返回。

    ⛔ 定型:收紧已发布契约 ⇒ 踩人工底线

    卡片自己标了 needs:contract-review(Clause ②,accept-set 变更),本席采纳并加上该标。⛔ 分诊席不裁决。

    ⚠️ 这是 breaking 的:任何今天靠 index signature 传额外键的调用方会开始报错。卡片要求 changeset 里写明 semver 判断并附上普查作为依据 —— 这条请当作硬要求。

    ⛔ 给执行席的边界 —— 卡片的第 1 步是硬前置

    1. ⭐ 先普查再选形状,⛔ 不要先删 index signature:

      Measure first: every consumer that passes or reads a non-dollar key through QueryParams (grep the adapters, the console and the apps; the index signature may be carrying real extras for one adapter). Report the census before choosing.

      ⚠️ 这次普查决定第 2 步是"直接删"还是"换成具名扩展点"——两者的迁移成本差一个数量级。

    2. 三条候选形状(卡片给的,都可接受):删掉 index signature / 换成一个具名的 extra bag / 用模板字面量键类型只放行 $-前缀扩展。⭐ 第三条最贴合实际语义(convertQueryParams 读的就是 dollar-prefixed 键),值得优先评估。

    3. 调用方崩了怎么办,卡片给了二分法,照用:"either a real extra (route it through the named extension point) or a latent typo (fix it and say so — those are the wins)"。⭐ 后一类是这次改动的收益,请在 PR 里逐条列出来 —— 那是证明这次收紧值得的最好材料。

    4. ⛔ 不在范围内(卡片明列):指南本身(PR fix(skills): teach the honest test-double idiom in testing.md, retiring its three bare-any rows #7496 已覆盖测试指南);$filter 自己的值类型(观察:QueryParams.$filter 声明为 Record< string, any >,但仓内下沉口早已往里送 ObjectQL AST 数组 #3909,已关闭但是另一个轴)。

    5. ⛔ 不要用 as any 或 @ts-expect-error 让调用方通过 —— 那会把一处宽契约换成 N 处逃逸,且门禁看不见。

    ⛔ 分诊席不认领、不派单、不写码、不裁决 accept-set 变更。


    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

No one assigned

    Labels

    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanefindingpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions