Repository navigation
Commit d887aa9
fix(spec)!: HttpFindQueryParamsSchema declares the count and select values the list door accepts (#26901)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwB4PUNmhZj9zTbsZNNYma5Qa)_
Fixes #26817
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — `skills/**` teaches only the boolean count
(`skills/objectui/guides/data-integration.md:90`, `$count: true`) and
never a blank `select`; the generated reference page
`content/docs/references/api/protocol.mdx` is regenerated in this diff.
**BREAKING** (accept-set narrowing of a published declaration, `patch`):
`HttpFindQueryParamsSchema` refuses a `count` other than true/false and
a `select` naming no field, which it admitted. The list door already
refused both with a 400 (since #26811). ADR-0087 disposition in the
changeset: `not-required (no-migration-prescription)`.
## 维护者速读
- **改了什么**:`GET /data/:object` 查询参数的公开声明 `HttpFindQueryParamsSchema`
收窄:`count` 只收 `true`/`false`(字符串 `'false'` 读作 false),`select`
必须至少点名一个字段,空串、纯空格、只有逗号的值都被拒。
- **为什么改**:服务端列表接口早已对这些值回 400,但声明还写着「什么都收」,`'false'` 甚至被读成
true;按声明生成的客户端或 AI 作者会写出运行时被拒的请求。
- **风险与回滚**:只影响用这个 schema 解析查询串的代码;接口运行时行为不变(声明没有运行时读者)。回滚只需还原
`packages/spec/src/api/protocol.zod.ts` 里该块的两行声明。
-
**先看**:`packages/spec/src/api/protocol.zod.ts`(`HttpFindQueryParamsSchema`
的 `select`、`count`
两行)、`packages/spec/src/api/protocol.test.ts`(新增用例)、`.changeset/26817-http-find-query-params-count-select.md`。
- **两分钟验证**:`pnpm --filter @objectstack/spec exec vitest run
src/api/protocol.test.ts`,预期 215 个用例全绿,其中 `reads count="false" as
false`、`refuses count="maybe"`、`refuses select=","` 都在。
- **需要你做的**:无,席位落地。
## What changes
- **`count`** reads the door's accept set from the one declaration the
door parses, `QueryTransportParamsSchema.shape.count` (which
`findData`'s `countFlagValue` checks): the boolean, or the strings
`'true'` / `'false'`. The nullable arm is unwrapped (a querystring
carries no `null`), and the value is lowered to the boolean it means, so
`'false'` parses as `false`. It replaces `z.coerce.boolean()`, which
read `'false'` as `true` and admitted `'maybe'`, `'1'` and a blank
value.
- **`select`** must name at least one field:
`z.string().regex(/[^\s,]/)`. This is the door's own rule measured in
`projectionFieldsValue`: it splits on commas, trims each member, drops
empty members and refuses only when nothing is left. So a blank,
whitespace-only or comma-only value is refused, and `name,,email` or `
name , email ` stay accepted, as at the door. `\s` is the set `trim()`
strips. A regex is a string format check, not a `.refine()`, so it is
published as a JSON Schema `pattern` and adds no `dropped-refinements`
row.
- **`.describe()` texts** state the new accept sets; the reference page
regenerates from them.
## Tests
- `packages/spec/src/api/protocol.test.ts`: `count`
`'true'`/`'false'`/`true`/`false` parse to `true`/`false`; `'maybe'`,
`'1'`, `'0'`, `''`, `'TRUE'`, `1`, `null` refused; `select` `''`, `' '`,
`','`, `' , ,'`, tab refused; `name`, `name,email`, ` name , email `,
`name,,email` accepted unchanged.
## Acceptance notes
- **Readers of `HttpFindQueryParamsSchema`.** Regenerated:
`content/docs/references/api/protocol.mdx` (`gen:docs`, in this diff).
Unchanged by construction (key-level, keys unchanged):
`authorable-surface`, `api-surface`, `declaration-map`,
`export-origins`, `json-schema.manifest`,
`dropped-refinements.baseline.json`; `check:generated` and the build are
green. Prose only: `packages/client/src/index.ts:371`,
`packages/client/src/client.test.ts:1969`,
`packages/runtime/src/domains/data.ts:410`,
`packages/spec/src/data/query.zod.ts:215`,
`ui/packages/app-shell/src/console/organizations/manage/orgEnvironments.ts:64`,
CHANGELOG entries. Test reader:
`packages/metadata-protocol/src/protocol.page-window-declared.test.ts`
(top/skip only), green.
- **Clients in this repo.** No client sends `count` at all, and none
sends a non-boolean count string (`git grep` over `packages`,
`examples`, `apps`). `ObjectStackClient.data.find` and
`ScopedEnvironmentClient.data.find`
(`packages/client/src/index.ts:7060`, `:7946`) send `select` as
`select.join(',')` whenever `select` is truthy, so `select: []` sends
`?select=`. Not changed here: the door answers it with a 400 naming
`select`, and dropping it client-side would turn an empty projection
into "every column", the widening #26811 closed. Reported to the seat
for a ruling.
- **Siblings, out of scope.** `GetDataRequestSchema.select`
(`z.array(z.string())`) is on the by-id door (`getData`), not
`findData`, and admits `[]` that `getData` refuses.
`QueryTransportParamsSchema.select` / `$select` (and the canonical
`fields`) sit on the same `findData` door and admit a blank string or
`[]`, but they also feed `QueryWithTransportSchema` and the `POST
/data/:object/query` body declaration, outside this card's file surface.
Both are reported to the seat, not folded.
- **Clause-② and Metadata-protocol.** `no (narrowing)`: only a published
accept set shrinks, nothing is widened, no key moves.
`Metadata-protocol: none`: this is a querystring transport declaration,
not metadata an author stores; no metadata type, key or stored shape
changes.
## Verification
Final head: `c3c1f10ae` (merge of `origin/main` 5787df4).
<details><summary>Spec tests and typecheck</summary>
`pnpm --filter @objectstack/spec test` then `typecheck` (lock VERDICT
command-exit 0 for both; the chain's third leg needed a full build):
Test Files 735 passed (735), Tests 22061 passed, 1 todo. After the full
build: `metadata-protocol` `protocol.page-window-declared.test.ts` +
`protocol.empty-projection-unknown-count.test.ts`: 2 files, 532 tests
passed. Full `pnpm build`: Tasks 116 successful, 116 total.
</details>
<details><summary>Derived gates (103) at c3c1f10</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 103 families, each run with the exit code
captured before any pipe; all 103 exit 0 after the full build. `--ran`:
"103 derived famil(ies) accounted for — 103 run, 0 NOT-MEASURED".
Local-only: `check:dev-prereqs` exit 0, `check:skill-frame-freshness`
exit 0. Before the build, on 794bcd8, 97 exit 0 and 6 exit 3 (unbuilt
dist).
</details>
<details><summary>Full dogfood at c3c1f10</summary>
`pnpm --filter @objectstack/dogfood test` under the lock: Test Files 1
failed, 326 passed, 3 skipped (330); Tests 2 failed, 2681 passed, 20
skipped (2703). Both failures are
`packaged-action-disabled-dispatch-live.dogfood.test.ts` (acceptance[0]
and acceptance[5]/[2]), a file added by #26829 at 18:17Z today. It reads
no `count` or `select`, and `HttpFindQueryParamsSchema` has no runtime
reader. The same file re-run alone on the same head and dist: 1 file, 8
tests passed. Read as load-dependent flake, not this diff.
</details>
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzh5dnFHGxFUBugn12b
Co-authored-by: Claude <noreply@anthropic.com>1 parent 452325e commit d887aa9
4 files changed
Lines changed: 65 additions & 5 deletions
File tree
- .changeset
- content/docs/references/api
- packages/spec/src/api
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2043 | 2043 | | |
2044 | 2044 | | |
2045 | 2045 | | |
2046 | | - | |
| 2046 | + | |
2047 | 2047 | | |
2048 | 2048 | | |
2049 | 2049 | | |
2050 | 2050 | | |
2051 | 2051 | | |
2052 | 2052 | | |
2053 | 2053 | | |
2054 | | - | |
| 2054 | + | |
2055 | 2055 | | |
2056 | 2056 | | |
2057 | 2057 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
692 | 692 | | |
693 | 693 | | |
694 | 694 | | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
695 | 721 | | |
696 | 722 | | |
697 | 723 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
2369 | 2369 | | |
2370 | 2370 | | |
2371 | 2371 | | |
2372 | | - | |
| 2372 | + | |
| 2373 | + | |
| 2374 | + | |
| 2375 | + | |
| 2376 | + | |
| 2377 | + | |
| 2378 | + | |
| 2379 | + | |
2373 | 2380 | | |
2374 | 2381 | | |
2375 | 2382 | | |
| |||
2384 | 2391 | | |
2385 | 2392 | | |
2386 | 2393 | | |
2387 | | - | |
| 2394 | + | |
| 2395 | + | |
| 2396 | + | |
| 2397 | + | |
| 2398 | + | |
| 2399 | + | |
| 2400 | + | |
| 2401 | + | |
| 2402 | + | |
2388 | 2403 | | |
2389 | 2404 | | |
2390 | 2405 | | |
| |||
0 commit comments