Repository navigation
fix(spec)!: EngineQueryOptionsSchema.fields and the RPC request's select refuse an empty projection, as their transport siblings do - #27007
Conversation
…t declarations EngineQueryOptionsSchema.fields and RpcQueryOptionsSchema.select take the projection's array arm (at least one name, none blank) from the declaration their transport siblings already use. The shared arm moves above EngineQueryOptionsSchema because RpcQueryOptionsSchema extends it at module load. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzfjH9js8WRa8Xi2dmx
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 147 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0c987b6e7b8af0bb4e2dc1352a1c4b151c366b2f && git checkout 0c987b6e7b8af0bb4e2dc1352a1c4b151c366b2f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e6c440900072a2d2423474962db9c7db6011b845 ba84865301c609c8167b187aebdf0109f34dbd37 && git checkout -B drift-repro e6c440900072a2d2423474962db9c7db6011b845 && git merge --no-ff ba84865301c609c8167b187aebdf0109f34dbd37
node scripts/docs-audit/affected-docs.mjs --json e6c440900072a2d2423474962db9c7db6011b845
|
…lds with the projection rule DataEngineQueryOptionsSchema.select and DataEngineVectorFindRequestSchema.fields take the same array arm (at least one name, none blank) as their engine-side and transport siblings. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzfjH9js8WRa8Xi2dmx
Contract reviewServed-tier: Inputs read: card #26954 body and all four comments (triage 6456603692, claim 6456968829 with the seat ruling "align, do not retire", os-dev-reports 6457855300 and 6458595611); predecessor card #26909, PR #26949 (merged ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
维护者速读 (席位终稿)
席位意见:接受。契约复核 PASS(6458779713)。复核提出的两个提示已处理:PR 描述里 席位核对 · ACCEPT ·
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 40621685239 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 40623580358 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 40625584826 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Requested by Yibo Wang · project thread
Fixes #26954
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — the skills teach only a non-empty engine
fieldsprojection (skills/objectstack-query/rules/filters.md:105,fields: ['id'];skills/objectstack-query/SKILL.md:25listsfieldsamong the legalfindoption keys) and never an empty one, so no skill sentence goes false; the one transport data-read line,skills/objectui/guides/data-integration.md:82($select: ['name', 'email']), stays true too.维护者速读
EngineQueryOptionsSchema.fields、RPC 请求里已弃用的select、旧版DataEngineQueryOptionsSchema.select、向量检索请求的fields)不再接受空数组和带空白名的数组,与 fix(spec)!: transport select / $select and GetDataRequestSchema.select declare the projection the doors accept #26949 收窄的三份兄弟声明共用同一条规则(同一处定义,未复制)。engine.find(obj, { fields: [] })仍读全部列,不变。回滚 = revert 本 PR。packages/spec/src/data/data-engine.zod.ts(共享规则上移到EngineQueryOptionsSchema之前,以及四个成员)、packages/spec/src/data/data-engine.test.ts(新 describe 块)、.changeset/26954-engine-projection-empty.md。pnpm --filter @objectstack/spec exec vitest run src/data/data-engine.test.ts -t 'engine-side projection',预期 44 个用例全绿:空数组与空白名被拒且报错文字与兄弟声明一致,非空列表通过。What changes
packages/spec/src/data/data-engine.zod.ts:PROJECTION_NAMES_NO_FIELD,TransportProjectionStringSchema,transportProjectionArray,PROJECTION_ACCEPT_SETand its docblock) moves from just above the page-window block to just aboveEngineQueryOptionsSchema. It is not copied. It has to move:RpcQueryOptionsSchemais a non-lazyEngineQueryOptionsSchema.extend(...), so importing the module runs the lazy factory before any laterconstis initialised (a TDZReferenceError). The docblock gains one paragraph that says so and names the four new readers.EngineQueryOptionsSchema.fields:z.array(FieldNodeSchema)becomestransportProjectionArray(FieldNodeSchema), the same construction asQueryTransportParamsSchema.$select.RpcQueryOptionsSchema.select(@deprecated):z.array(z.string())becomestransportProjectionArray(z.string()), the same construction asQueryTransportParamsSchema.selectand the armGetDataRequestSchema.selectreads.DataEngineQueryOptionsSchema.select(@deprecatedlegacy options) andDataEngineVectorFindRequestSchema.fields(thevectorFindarm ofDataEngineRequestSchema). Both change fromz.array(z.string())totransportProjectionArray(z.string()).packages/spec/src/data/data-engine.test.ts: one newdescribeblock (44 cases), described under Tests..changeset/26954-engine-projection-empty.md:@objectstack/specpatch, with both declaration lines, one ADR-0087 marker and a BREAKING banner with Before / Now / Unchanged.No
.describe()changes. None of the four members had one, andcheck:generatedreports all 14 artifacts up to date on the head, so no reference page regenerates. A sentence saying "is refused" would also be false of the in-process engine (see Acceptance notes).The ADR-0049 note on
DataEngineRequestSchema("every arm below now has zero readers in this repo … an ADR-0049 enforce-or-remove candidate") is unchanged. I re-measured it on this head: no code outsidepackages/specparsesDataEngineRequestSchema,DataEngineFindRequestSchema,DataEngineFindOneRequestSchemaorDataEngineContractSchema(git grep, non-test: the hits outsidedata-engine.zod.tsare the generated reference page's import line, the export ledgers and CHANGELOG prose). So every sentence in it is still true. The note moved down by 33 lines with the block.Why
After #26817 and #26909, every published transport declaration of a read's projection refuses an empty one. These four engine-side declarations still admitted
[]and blank names, so a client generated from their JSON Schema, or an AI author reading them, learned the opposite rule. Seat ruling on #26954: align, do not retire.Readers and producers (measured on
origin/main3217fc5)EngineQueryOptionsSchemais read at runtime only for its key list (runtime/src/action-execution.ts:2047,.shapekeys) and by objectql'sengine-unknown-option.test.ts(keys). Key sets do not change.RpcQueryOptionsSchemais module-private and reached only through the three request schemas above, which have no parser outside spec.DataEngineQueryOptionsSchemaandDataEngineVectorFindRequestSchemahave no reference outsidepackages/specexcept the generated reference page's import line and one prose sentence incontent/docs/kernel/contracts/data-engine.mdx:637, which names the legacy schema as deprecated and is still true.ObjectQL.find/findOnenever parse their options with this schema.engine.ts:13563projects only whenast.fields.length > 0, and:13573falls back to*on an empty projection. Soengine.find(obj, { fields: [] })reads every column, as before.z.input/z.inferstaystring[]for all four members, so no TypeScript caller changes.pnpm --filter @objectstack/spec typecheckpasses on the head.packages/clientimports none of these schemas. JSON Schema gainsminItems: 1and a per-item\Spattern. Both are amin_lengthand astring_format, notcustom, so there is nodropped-refinementsrow.api-surface,declaration-map,export-originsandauthorable-surfaceare key- or export-level and unchanged (check:generated: 14/14 up to date).gen:openapiis ungated.[]: none.git grepoverpackages,ui,examples,appsandskills(non-test) forfields: []/select: []: every hit is UI, flow, analytics or result metadata, not an engine query.EngineQueryOptionsSchema,DataEngineQueryOptionsSchemaor the request schemas (git grepoverpackages/spec/src). The authored projection isQuerySchema.fields(views, reports), which this PR does not touch. SoMetadata-protocol: none, and ADR-0087 isnot-required (no-migration-prescription).Tests
packages/spec/src/data/data-engine.test.ts,describe('the engine-side projection declarations admit exactly what their transport siblings admit'):EngineQueryOptionsSchema.fieldsrefuses[],['name', '']and['name', ' ']. The issue path is['fields']or['fields', 1]. The message equals the oneQueryTransportParamsSchema.$selectgives for the same value, and that sibling's path is asserted too. It keeps['name']and['name', 'email']unchanged.DataEngineFindRequestSchema,DataEngineFindOneRequestSchemaand theDataEngineRequestSchemaunion:query.selectrefuses the same three values at['query', 'select', …], with the bareselectsibling's message;query.fieldsrefuses them at['query', 'fields', …], with$select's message;query.select: ['name']/['name', 'email']parses and folds ontofields.DataEngineQueryOptionsSchema.selectrefuses the same three values at['select', …], with the bareselectsibling's message, and keeps a non-empty list unchanged.DataEngineVectorFindRequestSchemaand theDataEngineRequestSchemaunion (method: 'vectorFind'):fieldsrefuses the three values at['fields', …], with the bareselectsibling's message, and keeps a non-empty list unchanged.Ablation, after committing the fix, through
scripts/ablation-replace.mjs(anchor hit, blob moved, restored to the HEAD blob withgit diff HEADempty):fieldsback toz.array(FieldNodeSchema): 12 failed / 17 passed in the block.selectback toz.array(z.string()): 9 failed / 20 passed.ba848653(the 44-case block):DataEngineQueryOptionsSchema.selectreverted gives 3 failed / 41 passed;DataEngineVectorFindRequestSchema.fieldsreverted gives 6 failed / 38 passed. Both restored to the HEAD blob, withgit diff HEADempty.Runs on head
ba848653:@objectstack/specfull suite under the lock: 819 files, 23498 tests passed (1 todo). Thentypecheck(tsc, scripts and test typecheck):VERDICT command-exit 0.pnpm --filter @objectstack/spec build && check:generated:VERDICT command-exit 0, and all 14 generated artifacts are up to date, so nothing was regenerated.(narrowing)arm: built the@objectstack/dogfood^...closure, then ranpnpm --filter @objectstack/dogfood testunder the lock with no shard and no path. Result: 344 files passed, 3 skipped (347); 2784 tests passed, 20 skipped (2804);VERDICT command-exit 0.dispatch-gates --commands: 80 derived families. All 80 exit 0. Three of them (check:doc-formula-expressions,check:dual-build-cjs-loads,check:lean-entry-closure) first exited 3 because their prerequisite was not met. They were run again after the dogfood closure build and exited 0.--ranreconciliation: 80 run, 0 NOT-MEASURED.17ce2dfb, had the same readings: 23483 spec tests, 80/80 gates, and the same dogfood counts.Acceptance notes
engine.find(obj, { fields: [] })still reads every column, because the engine does not parse its options. Everything the new declaration admits, the engine accepts. Nothing the engine does contradicts a sentence in this diff. No.describe()claims "is refused".DataEngineQueryOptionsSchema.select,DataEngineVectorFindRequestSchema.fields) are aligned in this PR, at the seat's request.fields: []as every column is runtime behaviour outside this card. The seat filed it as objectql: the in-process engine readsfields: []as every column while every published projection declaration refuses an empty projection (found on #26954) #27008.Human Minimum
Each item is one rule from
CONTRIBUTING.md › Human Minimum,
which links the AGENTS.md text that states it.
packages/specholds no runtime logic.pnpm lintpasses. (No formatter ran.pnpm lintis CI's repo-wide run.)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed (check:generated: none were stale).content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzfjH9js8WRa8Xi2dmx